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。)
EF6(代码优先)、MVC、Unity 和没有存储库的服务层