EF 代码优先和 MVC 4 更好的加载性能

Posted

技术标签:

【中文标题】EF 代码优先和 MVC 4 更好的加载性能【英文标题】:Better loading performance with EF code first and MVC 4 【发布时间】:2013-07-15 16:55:30 【问题描述】:

我试图在我的 MVC 4 项目中做出更好(= 更快)的响应,主要是在 Web Api 部分。我添加了 MiniProfiler 以查看加载缓慢的问题,但我无法弄清楚。

                                                    duration (ms)   from start (ms)     query time (ms)
http://www.url.com:80/api/day?city=param (example)  1396.1             +0.0     1 sql   173.8
                                        logging     9.3              +520.9     
                                        EF query    4051.5           +530.2     2 sql   169.6 

然后当我再次尝试相同的网址时,我有这些数字:

 http://www.url.com:80/api/day?city=param (example) 245.6              +0.0     1 sql   50.6
                                         logging    8.6               +19.6     
                                         EF query   7.7               +28.3     

但是当我在 2 分钟后尝试它时,我再次得到了第一个示例中的大数字。

与加载主页索引相同:

http://www.blanskomenu.amchosting.cz:80/    333.0   +0.0    
         Controller: HomeController.Index   71.0    +286.8  
                          Find: Index   100.4   +387.8  
                     Render : Index     2468.1  +494.6 

这是我在第一个示例中使用 Web Api 的方法

[OutputCache(CacheProfile = "Cache1Hour", VaryByParam = "city")]
    public IEnumerable<RestaurantDayMealsView> GetDay(string city)
    
        var profiler = MiniProfiler.Current;
        using (profiler.Step("logging"))
        
            var logFile = new LogFile(System.Web.HttpContext.Current.Server.MapPath("~/Logs/"), DateTime.Today);
            logFile.Write(String.Format("0,api/daymenu,1", DateTime.Now, city));
        
        using (profiler.Step("EF query"))
        
            var meals = repo.GetAllDayMealsForCity(city);
            if (meals == null)
            
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            
            return meals;
        
    

和我的存储库方法:

    public IEnumerable<RestaurantDayMealsView> GetAllDayMealsForCity(string city)
    
        return db.Restaurants
                 .Include(rest => rest.Meals)
                 .Where(rest => rest.City.Name == city)
                 .OrderBy(r => r.Order)
                 .AsEnumerable()
                 .Select(r => new RestaurantDayMealsView()
                     
                         Id = r.Id,
                         Name = r.Name,
                         Meals = r.Meals.Where(meal => meal.Date == DateTime.Today).ToList(),
                         IsPropagated = r.IsPropagated
                     ).Where(r => r.Meals.Count > 0);
    

对于我的主页索引,我的控制器中只有:

    public ActionResult Index()
    

        return View();
    

所以我的问题是:

为什么渲染索引需要这么长时间?我只有默认网站,所以我认为 css 和其他东西没有问题。

什么是 EF 查询而不是查询时需要这么长时间?我该如何解决这些问题?

我正在查看这些链接:SO list 和 ASP.NET MVC Overview - performence,我尝试了一些技巧并阅读了其他一些技巧,但没有什么对我有太大帮助。问题可能与托管有关吗?还是在哪里?谢谢

【问题讨论】:

我发现 EFProf 在分析实体框架查询时非常有用:hibernatingrhinos.com/products/EFProf。您可以使用免费试用版来看看发生了什么 【参考方案1】:

您的存储库方法中似乎存在 1+N 查询问题。仅当您不修改集合时才优化使用Include(即在其上使用Where 之类的东西)。当您这样做时,EF 将从数据库中重新获取记录。您需要先将Meals 转换为List,然后运行Where 子句。这实际上会冻结 Meals 的预选结果,然后在内存中而不是在数据库中过滤它们。

Meals = r.Meals.ToList().Where(meal => meal.Date == DateTime.Today).ToList(),

【讨论】:

【参考方案2】:

1. 在您的Repository.GetAllDayMealsForCity() 方法中:

return db.Restaurants
      .Include(rest => rest.Meals)
      .Where(rest => rest.City.Name == city)
      .OrderBy(r => r.Order)
      .AsEnumerable() // <-- Materiazling the query before projection
      .Select(r => new RestaurantDayMealsView()
          
          Id = r.Id,
          Name = r.Name,
          Meals = r.Meals.Where(meal => meal.Date == DateTime.Today).ToList(),
          IsPropagated = r.IsPropagated
          ).Where(r => r.Meals.Count > 0);

您使用Select 方法在Projecting 之前调用AsEnumerable() 的结果。您必须记住 AsEnumerable() 导致查询“物化”(执行),并且因为您在 Select 方法之前调用它,所以您的查询不会将结果限制为仅 RestaurantDayMealsView 所需的数据(进一步的投影是在内存对象上完成的,而不是在数据存储上)。

另外,您最后的Where 也可以附加在AsEnumerable() 方法之前。

2. 第一次和第二次命中之间的分析结果存在显着差异的原因可能是,在 Entity Framework 第一次从 SQL Server 查询数据后,它在内部将结果缓存在内存中以获得更好的性能。

【讨论】:

我认为你的第一点是对的,但它仍然对我的结果加载没有多大帮助(它有助于“少数”毫秒)。

以上是关于EF 代码优先和 MVC 4 更好的加载性能的主要内容,如果未能解决你的问题,请参考以下文章

MVC3 代码优先 - EF4.1 不会自动创建表(使用 MySQL 和 Connector/Net 6.3.6。)

EF 4.1 代码优先关系表

EF6(代码优先)、MVC、Unity 和没有存储库的服务层

实体框架代码优先的性能

如何使用 EF Core 代码优先迁移为 ASP.NET Core MVC 配置 N 层架构

ASP.NET MVC 3 EF 代码优先 - 掌握详细信息 CRUD