C# 实体框架5性能问题
现在我正在处理一个相当复杂的数据库。我们的对象模型被设计为映射到数据库。我们将EF5用于手动生成的POCO类 一切正常,但也有人对表演不满。我从来没有遇到过EF的性能问题,所以我想知道这一次我是做错了什么,还是这个问题可能存在于其他地方 主查询可以由动态参数组成。我有几个if和switch块,它们在概念上是这样的:C# 实体框架5性能问题,c#,sql,asp.net-mvc,performance,entity-framework,C#,Sql,Asp.net Mvc,Performance,Entity Framework,现在我正在处理一个相当复杂的数据库。我们的对象模型被设计为映射到数据库。我们将EF5用于手动生成的POCO类 一切正常,但也有人对表演不满。我从来没有遇到过EF的性能问题,所以我想知道这一次我是做错了什么,还是这个问题可能存在于其他地方 主查询可以由动态参数组成。我有几个if和switch块,它们在概念上是这样的: if (parameter != null) { query = query.Where(c => c.Field == parameter); } 另外,对于一些复杂和/或
if (parameter != null) { query = query.Where(c => c.Field == parameter); }
另外,对于一些复杂和/或组合,我使用Albahari的LinqKit扩展
该查询针对的是一个大的“订单”表,其中包含了一年又一年的数据。不过,平均使用时间为2个月
现在,当主查询组成时,将使用Skip/Take
组合对其进行分页,其中Take设置为10个元素
在所有这些之后,IQueryable通过层发送,到达使用Automapper的MVC层
在这里,当Automapper开始迭代时(从而真正执行查询),它会调用一组导航属性,这些属性有自己的导航属性等等。根据EF建议,所有内容都设置为延迟加载,以避免在包含3或4个以上不同实体的情况下急于加载。我的场景是这样的:
if (parameter != null) { query = query.Where(c => c.Field == parameter); }
- 订单(最多10个)
- 许多导航属性都已订购
- 其中一些下有其他导航(本地化实体)
- 订单详细信息(每个订单有多个订单详细信息)
- 每个订单详细信息下的许多导航属性
- 其中一些下有其他导航(本地化实体)
- 每个订单详细信息下的许多导航属性
- 许多导航属性都已订购
- 延迟加载的属性是按顺序调用的,而不是并行化的,因此需要更多的时间
- 作为前一点的结果,每个查询之间都有一些死时间,因为数据库必须为每个查询接收sql、运行sql、返回sql等等
- 表拆分以减少所选列
- 关闭对象跟踪,因为此场景为只读(具有未跟踪的实体)
所以,如果任何人有任何提示、建议、最佳实践,我都不知道,或者可以告诉我,在这个场景中使用EF和延迟加载是大错特错的。。。大家都欢迎。
考虑一下,EF肯定有助于使开发时间更快。但是,您必须记住,当您从数据库返回大量数据时,EF使用的是动态SQL。这意味着EF必须为1。创建SQL,2.SQL Server然后需要创建一个执行计划。这发生在查询运行之前
在使用存储过程时,SQL Server可以缓存执行计划(可以对其进行编辑以提高性能),这确实比使用EF更快。但是您始终可以创建存储过程,然后从EF执行它。我会将任何复杂的过程或查询转换为存储过程,然后从EF调用。然后,您可以看到您的性能提高,并从中重新评估。试着想出一个高效而简单的sql查询来获取视图的数据 有可能吗 如果没有,请尝试对表进行分解(反规范化),以减少获取数据所需的联接。此外,表列上是否有有效的索引以加快数据检索 如果是,请忘记EF,编写一个存储过程并使用它获取数据 对于只读方案,必须关闭所选查询的跟踪。看看我的数字: 正如您所看到的,跟踪和不跟踪场景之间的差异是显著的
我会尝试快速加载,但不是在所有地方(这样你就不会得到7k行长的查询),而是在选定的子查询中。对于一个产生大量分层数据的非常复杂的查询,如果你采取正确的方法,存储过程通常不会比LINQ/EF更好地提高性能。正如您所注意到的,EF(惰性和急切加载)的两个“开箱即用”选项在这种情况下不能很好地工作。但是,仍然有几种很好的方法可以优化此功能: (1) 与其将一组实体读入内存,然后通过automapper进行映射,不如尽可能直接在查询中进行“自动映射”。例如:
var mapped = myOrdersQuery.Select(o => new OrderInfo { Order = o, DetailCount = o.Details.Count, ... })
// by deferring the load until here, we can bring only the information we actually need
// into memory with a single query
.ToList();
// read with AsNoTracking() since we'll be manually setting associations
var myOrders = myOrdersQuery.AsNoTracking().ToList();
var orderIds = myOrders.Select(o => o.Id);
var myDetails = context.Details.Where(d => orderIds.Contains(d.OrderId)).ToLookup(d => d.OrderId);
// reassemble in memory
myOrders.ForEach(o => o.Details = myDetails[o.Id].ToList());
如果您只需要复杂层次结构中字段的子集,那么这种方法非常有效。此外,EF选择hierarchica的能力