【问题标题】:view taking over 3 mins to load查看需要超过 3 分钟才能加载
【发布时间】:2014-01-06 11:21:13
【问题描述】:

使用 asp.net MVC 4.0 c#,Visual Studio 2012 专业版。

好的,我的控制器中有一个单一的操作结果,目的是采用许多不同的服务和模型,并将数据反规范化为一个列表以供视图显示,这是基于用户交互通过用于过滤数据的简单控件。

整个网站已从 Webforms Sql 项目迁移到使用内部 CMS 解决方案的 MVC 代码优先方法(cms 基于 Nop commerce 和 Orchard)。

现在这个控制器功能工作正常,直到我为每个传递的模块添加代码。我最初想和旧项目一样,加入数据,然后交叉查询去规范化的结果,然后使用。

但是,当我首先从 SQL 转移到代码时,我不确定是否或如何模拟交叉查询。

这导致我创建了很多循环。 这是控制器动作方法和非动作。

 public ActionResult Index(UsersModel model) 
    {
        model.DateTo = model.DateTo.AddDays(1);

        if (model.DateFrom == null || model.DateTo == null || model.DateFrom == DateTime.MinValue || model.DateTo == DateTime.MinValue.AddDays(1))
        {
            // default to last 30 days
            model.DateFrom = _clock.UtcNow.AddDays(-30);
            model.DateTo = _clock.UtcNow;
        }

        var userQuery = _academyUserService.Query()
            .Where(x => x.Activity.DateRegistered >= model.DateFrom && x.Activity.DateRegistered <= model.DateTo);

        var quizCompletedQuery = _quizService.QueryQuizHistoryCompleted()
            .Where(x => x.DateCompleted >= model.DateFrom && x.DateCompleted <= model.DateTo);

        var quizHistoryQuery = _quizService.QueryHistory()
            .Where(x => x.DateCompleted >= model.DateFrom && x.DateCompleted <= model.DateTo);

        var moduleQuery = _moduleService.Query()
            .Where(x => x.Published);

        //admin country selected, null = global
        if (_moServices.WorkContext.HttpContext.Session["AdminAreaCurrentCulture"] != null)
        {
            userQuery = userQuery.Where(x => x.UserCountry.CountryCulture.Culture == _moServices.WorkContext.HttpContext.Session["AdminAreaCurrentCulture"].ToString());
            quizCompletedQuery = quizCompletedQuery.Where(x => x.Country.CountryCulture.Culture == _moServices.WorkContext.HttpContext.Session["AdminAreaCurrentCulture"].ToString());
            quizHistoryQuery = quizHistoryQuery.Where(x => x.Module.Country.CountryCulture.Culture == _moServices.WorkContext.HttpContext.Session["AdminAreaCurrentCulture"].ToString());
            moduleQuery = moduleQuery.Where(x => x.Country.CountryCulture.Culture == _moServices.WorkContext.HttpContext.Session["AdminAreaCurrentCulture"].ToString());
        }

        if (!string.IsNullOrEmpty(model.Name))
        {
            userQuery = userQuery.Where(x => x.FirstName.Contains(model.Name) || x.Surname.Contains(model.Name));
        }

        if (!string.IsNullOrEmpty(model.AccountType))
        {
            userQuery = userQuery.Where(x => x.AccountType.ToString() == model.AccountType);
        }

        if (!string.IsNullOrEmpty(model.Company))
        {
            userQuery = userQuery.Where(x => x.BusinessName == model.Company);//placeholder intefering with this?
        }

        if (!string.IsNullOrEmpty(model.Code))
        {
            userQuery = userQuery.Where(x => x.RegistrationCode.Contains(model.Code));
        }

        if(true)//cant condition an Iquery as its not a list...)//temp
        {
            //quizCompletedQuery = quizCompletedQuery.Where(x => x.Country.CountryCulture.Culture == _moServices.WorkContext.HttpContext.Session["AdminAreaCurrentCulture"].ToString());
        }

        //new code
        var groupedModules = moduleQuery
             .OrderBy(x => x.DisplayOrder)
             .ToList()
             .GroupBy(x => x.Country)
             .SelectMany(group => group.Select((x, i) => new { Index = i, Module = x }))
             .GroupBy(anon => anon.Index)
             .Select(group => group.Select(x => x.Module).ToList())
             .ToList();

        var historyResults = quizHistoryQuery.ToList();

        //my old code
        PrepareBusinessNames(model);

        var usersToModel = userQuery.ToList().Select(x =>
        {

            bool thisHistoryPassed1 = false;
            bool thisHistoryPassed2 = false;
            bool thisHistoryPassed3 = false;
            bool thisHistoryPassed4 = false;

            if (groupedModules.Count > 0)
            {
                var module1 = groupedModules[0];
                var moduleIds1 = module1.Select(y => y.Id).ToList();
                var thisHistory1 = quizHistoryQuery.Where(y => y.User.Id == x.Id && moduleIds1.Contains(y.Module.Id));
                thisHistoryPassed1 = thisHistory1.Any(_quizService.IsHistoryPassed);
            }

            if (groupedModules.Count > 1)
            {
                var module2 = groupedModules[1];
                var moduleIds2 = module2.Select(y => y.Id).ToList();
                var thisHistory2 = quizHistoryQuery.Where(y => y.User.Id == x.Id && moduleIds2.Contains(y.Module.Id));
                thisHistoryPassed2 = thisHistory2.Any(_quizService.IsHistoryPassed);
            }

            if (groupedModules.Count > 2)
            {
                var module3 = groupedModules[2];
                var moduleIds3 = module3.Select(y => y.Id).ToList();
                var thisHistory3 = quizHistoryQuery.Where(y => y.User.Id == x.Id && moduleIds3.Contains(y.Module.Id));
                thisHistoryPassed3 = thisHistory3.Any(_quizService.IsHistoryPassed);
            }

            if (groupedModules.Count > 3)
            {
                var module4 = groupedModules[3];
                var moduleIds4 = module4.Select(y => y.Id).ToList();
                var thisHistory4 = quizHistoryQuery.Where(y => y.User.Id == x.Id && moduleIds4.Contains(y.Module.Id));
                thisHistoryPassed4 = thisHistory4.Any(_quizService.IsHistoryPassed);
            }

            return new UsersSearchModel
            {
                UserID = x.Id,
                Name = x.FirstName,
                Surname = x.Surname,
                Company = x.BusinessName,
                AccountType = x.AccountType.ToString(),
                UserCode = x.RegistrationCode,
                VideosViewed = "", //x.VideoActivity.ToString(), 
                Module1 = thisHistoryPassed1,
                Module2 = thisHistoryPassed2,
                Module3 = thisHistoryPassed3,
                Module4 = thisHistoryPassed4,
                Module1Url = thisHistoryPassed1 ? Url.Content("~/Areas/Admin/Media/Images/checked.png") : Url.Content("~/Areas/Admin/Media/Images/unchecked.png"),
                Module2Url = thisHistoryPassed2 ? Url.Content("~/Areas/Admin/Media/Images/checked.png") : Url.Content("~/Areas/Admin/Media/Images/unchecked.png"),
                Module3Url = thisHistoryPassed3 ? Url.Content("~/Areas/Admin/Media/Images/checked.png") : Url.Content("~/Areas/Admin/Media/Images/unchecked.png"),
                Module4Url = thisHistoryPassed4 ? Url.Content("~/Areas/Admin/Media/Images/checked.png") : Url.Content("~/Areas/Admin/Media/Images/unchecked.png")
            };
        }).ToList();

        var jsonSerialiser = new JavaScriptSerializer();
        var jsonString = jsonSerialiser.Serialize(usersToModel);//or is it model? or a list of model?
        model.REFACTOR_ForJson = jsonString;

        return View(model);
    }

    #region utilities

    [NonAction]
    private UsersModel PrepareBusinessNames(UsersModel model)
    {
        if (_moServices.Authoriser.Authorise(DefaultPermissions.AccessAdminPanel))
        {
            var listItems = _academyUserService.GetAllBusinessNames().Select(x =>
            {
                return new SelectListItem
                {
                    Value = x,
                    Text = x
                };
            }).OrderBy(x => x.Value)
            .ToList();

            model.CurrentBusinessNames = new SelectList(listItems, "Value", "Text");
        }

        return model;
    }

如您所见,我想返回一个用户列表,并根据过滤器显示该列表,其中包含代表模块的 4 列以及是否通过。

正如我所说,当我添加代码以获取通过或未通过的布尔值时,我的查询已被拍摄并且需要很长时间。

那么,有没有办法通过调整现有代码来解决问题,或者是否可以使用与交叉查询类似的方法来重写功能?

感谢您的提示!

更新::

这是配置文件结果,因为在我不确定我在这里看什么之前我从未使用过它。

注释掉“if(groupedModules.Count > 0)”部分和其他 If 语句,这显然是导致响应缓慢的主要原因。所以正如我之前问过的,有没有更好的方法来实现我的结果?

更新:: 替换

if (groupedModules.Count > 0)
            {
                var module1 = groupedModules[0];
                var moduleIds1 = module1.Select(y => y.Id).ToList();
                var thisHistory1 = quizHistoryQuery.Where(y => y.User.Id == x.Id && moduleIds1.Contains(y.Module.Id));
                thisHistoryPassed1 = thisHistory1.Any(_quizService.IsHistoryPassed);
            }

到

if (groupedModules.Count > 0)
            {
                var module1 = groupedModules[0];
                var moduleIds1 = module1.Select(y => y.Id).ToList();
                var thisHistory1 = historyResults.Where(y => y.User.Id == x.Id && moduleIds1.Contains(y.Module.Id));
                thisHistoryPassed1 = thisHistory1.Any(_quizService.IsHistoryPassed);
            }

我没有使用 iquerable,而是列出历史然后使用列表。 这显着减少了加载时间,从 6 分钟到 15 秒。 然而,众所周知,这仍然很慢,唯一的结论是这段代码必须位于选择列表中。那么有没有办法重做这个过程?

【问题讨论】:

  • 是的,我的第一个想法是删除 tolist,但是这取得了 0 改进,我把它留在了,因为它是在周末被领先者添加的。
  • 您是否尝试过对此使用任何实际的性能测量工具?是否使用 SQL Profiler 和外部工具对查询进行了分析?

标签: c# asp.net-mvc-4 nhibernate code-first


【解决方案1】:

快速猜测:当您执行组查询时,数据仍处于查询形式。 EF 上的 GroupBy() 性能如此糟糕,强制它首先列出要快得多。

比快速猜测要短得多的长答案:简介!

【讨论】:

  • 是的,我也听说过,虽然没有看到对我的页面时间有影响。这基本上是当我输入模块的代码时,突然间它几乎停止了,这可能是这个和我重复循环模块的组合吗?
  • 说真的,简介!这并不难也不可怕:)。如果您使用的是 Visual Studio,您可以在分析下找到它。如果你不配置文件,你很可能会开始追鬼。如果这样做,您将立即知道问题出在哪里。如果这不能立即为您提供解决方案,您可以更专注地询问为什么 x 会变慢,而不是整个故事的哪一部分可能导致它。
  • 通过分析器屏幕截图显示,OP 实际上使用的是 NHibernate 而不是 EF。
  • 是的,这显然是 NHibernate 正在做的事情。不过我不知道NHibernate,所以我不知道。遵循配置文件跟踪中的热路径可能是一个好主意 - 它会引导您找到有问题的代码部分。
【解决方案2】:
var historyResults = quizHistoryQuery.ToList();

您似乎正在执行此查询并将结果存储在列表中,但您正在执行以下几行:

var thisHistory1 = quizHistoryQuery.Where(y => y.User.Id == x.Id && moduleIds1.Contains(y.Module.Id));

导致再次执行查询。

这只是突然出现的东西,我不知道它是否会导致您的速度变慢。无论如何,请重新检查您可能两次运行相同查询的任何实例,因为您似乎在几个地方执行此操作。考虑将结果存储在列表中并从那时起使用它们。 还可以考虑启动 SQL Profiler 并查看生成的 SQL 查询的外观以及哪个查询花费的时间最多。

【讨论】:

  • 快速更新,取出历史结果(我认为是旧的冗余代码)但已经产生了 0 差异,仍在寻找
猜你喜欢
  • 2016-02-13
  • 2018-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 2018-04-03
相关资源
最近更新 更多