Performance 我可以在EntityFramework中的ObjectSet上使用多少Include来保持性能?

Performance 我可以在EntityFramework中的ObjectSet上使用多少Include来保持性能?,performance,linq,entity-framework,asp.net-mvc-2,linq-to-entities,Performance,Linq,Entity Framework,Asp.net Mvc 2,Linq To Entities,我对我的个人资料页面使用以下LINQ查询: var userData = from u in db.Users .Include("UserSkills.Skill") .Include("UserIdeas.IdeaThings") .Include("UserInterests.Interest") .

我对我的个人资料页面使用以下LINQ查询:

var userData = from u in db.Users
                        .Include("UserSkills.Skill")
                        .Include("UserIdeas.IdeaThings")
                        .Include("UserInterests.Interest")
                        .Include("UserMessengers.Messenger")
                        .Include("UserFriends.User.UserSkills.Skill")
                        .Include("UserFriends1.User1.UserSkills.Skill")
                        .Include("UserFriends.User.UserIdeas")
                        .Include("UserFriends1.User1.UserIdeas")
                               where u.UserId == userId
                               select u;
它有一个很长的对象图,并使用了许多include。它现在运行得很完美,但是当网站有很多用户时,它会对性能产生很大影响吗


我是否应该以其他方式进行测试?

我建议您执行负载测试,并测量网站在压力下的性能。如果你对每个请求执行复杂的查询,你可以考虑缓存一些结果。

是的。如果展开主表行上的多个明细行,请避免使用“包含”

我相信EF会将查询转换为一个大连接,而不是几个查询。因此,最终将在details表的每一行上复制主表数据

例如:Master->Details。比如说,master有100行,Details有5000行(每个master 50行)

如果延迟加载详细信息,则返回100行(大小:master)+5000行(大小:details)

如果使用.Include(“详细信息”),则返回5000行(大小:master+Details)。基本上,主部分被复制了50多次

如果包含多个表,则会向上倍增


检查EF生成的SQL。

包含的查询返回单个结果集,包含的数量会影响大数据集从数据库服务器传输到web服务器的方式。例如:

假设我们有一个实体
客户(Id、姓名、地址)
和一个实体
订单(Id、客户Id、日期)
。现在我们要查询一位客户的订单:

var customer = context.Customers
                      .Include("Orders")
                      .SingleOrDefault(c => c.Id == 1);
结果数据集将具有以下结构:

 Id | Name | Address | OrderId | CustomerId | Date 
---------------------------------------------------
  1 |  A   |   XYZ   |    1    |     1      | 1.1.
  1 |  A   |   XYZ   |    2    |     1      | 2.1.
 Id | Name | Address | OrderId | CustomerId | Date | OrderLineId | LOrderId | LProductId | Quantity | ProductId | ProductName
------------------------------------------------------------------------------------------------------------------------------
  1 |  A   |   XYZ   |    1    |     1      | 1.1. |     1       |    1     |     1      |    5     |    1      |     AA
  1 |  A   |   XYZ   |    1    |     1      | 1.1. |     2       |    1     |     2      |    2     |    2      |     BB
  1 |  A   |   XYZ   |    2    |     1      | 2.1. |     3       |    2     |     1      |    4     |    1      |     AA
  1 |  A   |   XYZ   |    2    |     1      | 2.1. |     4       |    2     |     3      |    6     |    3      |     CC
这意味着,
客户
数据对于每个
订单
都是重复的。现在,让我们用另一个实体来扩展这个示例——“OrderLine(Id,OrderId,ProductId,Quantity)
Product(Id,Name)`。现在我们要查询客户的订单、订单行和产品:

var customer = context.Customers
                      .Include("Orders.OrderLines.Product")
                      .SingleOrDefault(c => c.Id == 1);
结果数据集将具有以下结构:

 Id | Name | Address | OrderId | CustomerId | Date 
---------------------------------------------------
  1 |  A   |   XYZ   |    1    |     1      | 1.1.
  1 |  A   |   XYZ   |    2    |     1      | 2.1.
 Id | Name | Address | OrderId | CustomerId | Date | OrderLineId | LOrderId | LProductId | Quantity | ProductId | ProductName
------------------------------------------------------------------------------------------------------------------------------
  1 |  A   |   XYZ   |    1    |     1      | 1.1. |     1       |    1     |     1      |    5     |    1      |     AA
  1 |  A   |   XYZ   |    1    |     1      | 1.1. |     2       |    1     |     2      |    2     |    2      |     BB
  1 |  A   |   XYZ   |    2    |     1      | 2.1. |     3       |    2     |     1      |    4     |    1      |     AA
  1 |  A   |   XYZ   |    2    |     1      | 2.1. |     4       |    2     |     3      |    6     |    3      |     CC
正如您所看到的,数据变得非常重复。通常,每个包含到引用导航属性(
示例中的产品
)将添加新列,每个包含到集合导航属性(
示例中的订单
订单行
)将为包含的集合中的每一行添加新列和重复已创建的行

这意味着您的示例可以很容易地拥有数百列和数千行,这是需要传输的大量数据。正确的方法是创建性能测试,如果结果不能满足您的期望,您可以通过自己的查询或
LoadProperty
方法分别修改查询和加载导航属性

单独查询的示例:

var customer = context.Customers
                      .Include("Orders")
                      .SingleOrDefault(c => c.Id == 1);
var orderLines = context.OrderLines
                        .Include("Product")
                        .Where(l => l.Order.Customer.Id == 1)
                        .ToList();
LoadProperty
示例:

var customer = context.Customers
                      .SingleOrDefault(c => c.Id == 1);
context.LoadProperty(customer, c => c.Orders);
此外,您应该始终只加载真正需要的数据

Edit:我刚才创建的目的是支持额外的即时加载策略,即时加载的数据将在额外的结果集中传递(由同一数据库往返中的单独查询创建)。如果您觉得这一改进很有趣,请不要忘记投票支持该提案。

(您可以通过从如下数据库创建2个或多个小型数据请求来提高许多Include的性能

根据我的经验,每个查询只能最多包含2个,如下图所示。超过这一点将导致非常糟糕的性能

var userData = from u in db.Users
                        .Include("UserSkills.Skill")
                        .Include("UserIdeas.IdeaThings")
                        .FirstOrDefault();

 userData = from u in db.Users
                    .Include("UserFriends.User.UserSkills.Skill")
                    .Include("UserFriends1.User1.UserSkills.Skill")
                    .FirstOrDefault();

以上内容将通过使用更多的行程将小数据集从数据库带到数据库。

包含的结果可能会改变:它取决于调用包含方法的实体。

就像Ladislav Mrnka提出的示例一样,假设我们有一个实体

客户(身份证、姓名、地址)

该映射到此表:

Id  |  Name   | Address
-----------------------
C1  |  Paul   |   XYZ   
Id |  CustomerId  | Total
-----------------------
O1 |      C1      |  10.00
O2 |      C1      |  13.00
和实体订单(Id、CustomerId、Total)

该映射到此表:

Id  |  Name   | Address
-----------------------
C1  |  Paul   |   XYZ   
Id |  CustomerId  | Total
-----------------------
O1 |      C1      |  10.00
O2 |      C1      |  13.00
关系是一个客户到多个订单

var customer = context.Customers
                      .Include("Orders")
                      .SingleOrDefault(c => c.Id == "C1");

e示例1:客户=>订单

var customer = context.Customers
                      .Include("Orders")
                      .SingleOrDefault(c => c.Id == "C1");
Linq将被转换为非常复杂的sql查询。

在这种情况下,查询将生成两条记录,并复制有关客户的信息

 Customer.Id   |   Customer.Name |    Order.Id |  Order.Total
-----------------------------------------------------------
     C1        |       Paul      |       O1    |    10.00     
     C1        |       Paul      |       O2    |    13.00   

e示例2:订单=>Customer

var order = context.Orders
                      .Include("Customers")
                      .SingleOrDefault(c => c.Id == "O1");
Linq将被转换为一个简单的sql连接。

在这种情况下,查询将只生成一条记录,没有重复的信息:

 Order.Id |  Order.Total |  Customer.Id   |   Customer.Name
-----------------------------------------------------------
     O1   |    10.00     |      C1        |       Paul    

+1我自己发现了这一点。创建一些较小的查询并分别执行它们总比逐个运行要好。但好的是,借助EF的魔力,它会自动为您构建对象图。因此,如果您在一个查询中加载您的用户,然后加载您的技能,它们会自动出现在彼此的页面中导航属性。(我假设这是EF,因为我首先使用代码)。@Generic Type Tea,我相信这是EF的通用类型。事实上,我认为他们在首次访问时构建导航属性…+5(如果我可以的话)我没有意识到这一点,并且习惯于相当天真地使用渴望加载。这是一个非常好的例子,关于“ORM”中的“R”有多少知识仍然需要有效地使用ORM-不幸的是…@MikeCole至少感谢你从这篇文章中受益。奇怪的是,你是对的,它确实有效。在我的例子中,组合的单个查询无法纠正sql,但两个单独的查询可以工作。