9

所以我的 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 上,我们已经实现了瞬态故障处理,但我想减少对数据库的调用数量,而不会降低响应时间性能。

这里的最佳做法是什么?

4

5 回答 5

14

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

最佳做法是

  1. 设置!应用范围!绩效目标
  2. 轮廓、基准和定位瓶颈
  3. 查看并微调瓶颈,让您以最少的工作获得最大的性能胜利。(根据我的经验,90% 的时间不是 tsql)

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

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


对于一些背景:

我在谷歌上搜索“渴望加载缓慢”并来到这里。这是我的结果:

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

急切加载:106ms

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

延迟加载获胜,故事结束。

于 2013-11-11T00:22:19.283 回答
4

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

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

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

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,它使用内部连接将联系人与客户连接起来,然后仅选择 Contact.Id、Contact.Name 和 Customer.Name 列。

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

于 2013-04-04T18:59:27.557 回答
0

急切加载会获取冗余的主数据。这将占用大量内存,尽管上下文中的对象图每个实体仅存储单个主数据,但 SQL 会在其盘中转储大量数据。我从这里拍摄了以下图片

在此处输入图像描述

如您所见,在 SQL 查询的结果集中,User 表的 Data 也与 UserDetails 表重复了一样多。这似乎是性能的差异化因素(在您的情况下,主列比详细表有更多的记录)。

如果性能是您主要关心的问题,我建议您在分别获取详细表的数据时使用具有相同 where 子句的LINQ 连接 所以在您的情况下:-

步骤1

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

然后 第2步

var solicitors = from s in context.Solicitors
join loanApp in context.LoanApplications
select s.columns
where loanApp. <<Same condition as in step 1 where clause>>

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

于 2013-08-08T10:30:26.873 回答
0

您是否考虑过使用 sql 视图?

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

需要注意的是,您的之前查询正在使用 where 子句访问 1 个表,2 个调用。在后面的查询中,它使用 where 子句访问 2 个表,1 个调用。您的后查询中有连接,可能需要不同的索引。

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

于 2013-04-03T04:39:32.917 回答
0

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

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

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
于 2013-04-03T03:44:02.957 回答