entity framework上的懒惰与急切加载性能

所以我的DbContext上有以下模型类:

贷款

每次我渲染一个LoanApplication对象列表时,我都会这样做:

var context = new MyContext(); var applications = context.LoanApplications.Where(d => d.PropertyThatIWantToFilter = localVariable); 

这将返回一个IQueryable,然后我在我的控制器方法调用中转换为这样的ViewModel:

 var vm = applications.Select(d => new LoanApplicationViewModel(d)); 

LoanApplicationViewModel构造函数内部,我接受实体对象并执行相应的映射。 问题在于,由于Solicitors集合是一个导航属性,因此每次实例化新的视图模型时都会对数据库进行调用。 每个应用程序的平均律师数量是两个,这意味着如果我呈现一个列出最后10个应用程序的表,那么该应用程序将大约18-20次访问数据库。

我认为必须有一个更好的方法来获得这个集合,所以我改变了我的原始查询以急切加载集合,如下所示:

 var applications = context.LoanApplications.Include("Solicitors").Where... 

尽管这会将对数据库的调用次数减少到只有一次, 但查询速度要慢得多,大约慢50%。

数据库托管在SQL Azure上,我们已经实现了瞬态error handling,但我希望减少对数据库的调用数量,而不会降低响应时间性能。

这里的最佳做法是什么?

“这里的最佳做法是什么?”

最佳做法是

  1. 设置!应用范围广! 绩效目标
  2. 轮廓,基准和定位瓶颈
  3. 检查并微调瓶颈,为您提供最佳性能,赢得最少的工作。 (根据我的经验,90%的时间不是tsql)

现在看起来似乎有点无关紧要,但从这个角度来看,哪种加载模式你在应用程序域中最佳化是正确的方法。

渴望/懒惰没有“最佳实践”。 这就是两个选项都可用的原因。 此外,如果tsql是你的瓶颈并且在渴望/懒惰之间切换仍然没有达到你的性能目标,你将需要在SSMS中查看大量其他工具,例如查询分析器和查询计划分析器。


对于某些背景:

我正在谷歌搜索“急切加载慢”来到这里。 这是我的结果:

 var foo = _context.Foos //.Include("Answers") //.Include("Attachments") .FirstOrDefault(q => q.Id == key); 

渴望加载:106ms

延迟加载:11ms + 5ms + 5ms

懒人装载获胜,故事结束。

除了在使用eager和lazy时产生巨大结果或大量调用的SQL语句之外,通过从结果中放入和映射到ObjectContext / DbContext中会发生巨大的工作。 这会导致巨大的性能损失,在检索大量数据时我无法真正推荐任何这些。

最佳解决方案是指定显式的Select调用。 但是,在不知道如何构建viewmodel对象的情况下,给出一个如何执行此操作的示例有点困难。 所以,我在这里做的是给你一个使用匿名对象作为查询结果的例子。

此示例为您提供有关联系人所属客户的信息的联系人。

 var contacts = context.Contacts.Where(row => row.CategoryId == 1) .Select(row => new { ContactId = row.Id, Name = row.Name, CustomerName = row.Customer.Name }).ToList(); 

此查询将生成一个SQL SELECT,它使用内部联接将Customer与Customer连接起来,然后仅选择Contact.Id,Contact.Name和Customer.Name列。

如果您不打算使用数据并将更改保存回相同的上下文,则此解决方案是从服务器检索数据的最有效方法。 它既不使用急切也不延迟加载。

如果你能以某种方式查询你的律师表并使用你已经获取的应用程序列表过滤查询,那么获取的实体将被缓存在你的上下文中,我相信这将被用于导航属性而不是命中数据库。

我不确定如何编写律师提取查询,但我在想这样的事情

 int[] applicationIDs = applications.Select(x => x.ID).ToArray(); var solicitors = context.Solicitors.Where(x => x.Applications.Any(y => applicationIDs.Contains(y.ID))).ToArray(); // added toarray to cause execution cause im never sure when the LINQ actually runs 

你考虑过使用sql视图吗?

我不太确定Sql Azure。 但是在sql server中,在没有适当索引的情况下连接2个表时可能会有性能损失。 也许这会发生在您的查询中。

需要注意的是,您之前的查询是使用where子句,2个调用访问1个表。 在after查询中,它使用where子句,1个调用访问2个表。 您的查询后加入了,可能需要不同的索引。

您可以创建一个sql视图以确保使用正确的索引。 然后让您的应用程序调用视图。 存储过程也可用于此目的,但不太适合这种情况。

急切加载获取冗余主数据。 虽然上下文中的对象图仅存储每个实体的单个主数据,但它会占用大量内存,但SQL会将大量数据转储到其中。 我从这里拍了下面的照片

在此处输入图像描述

如果你看到,Data of User表也在SQL查询的结果集中重复了UserDetails表的数量。 这似乎是差异性能因素(在您的情况下,主列有更多记录然后详细信息表)。

如果性能是您主要考虑的问题,我建议您在使用相同的where子句时使用LINQ join ,同时单独获取详细信息表的数据。所以在您的情况下: –

步骤1

  var context = new MyContext(); var applications = context.LoanApplications.Where(d => d.PropertyThatIWantToFilter = localVariable); 

然后是step2

 var solicitors = from s in context.Solicitors join loanApp in context.LoanApplications select s.columns where loanApp. <> 

谢谢,你的问题让我回顾了我自己的代码:-)