我在我的个人资料页面使用以下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;
它有一个长对象图并使用许多包含。它现在运行得很好,但是当网站有很多用户时,它会影响性能吗?
我应该用其他方式吗?
答案 0 :(得分:84)
包含includes的查询返回单个结果集,包含的数量会影响从数据库服务器向Web服务器传输大数据集的方式。例如:
假设我们有一个实体Customer (Id, Name, Address)
和一个实体Order (Id, CustomerId, Date)
。现在我们想用她的订单查询客户:
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.
这意味着每个Cutomers
都会重复Order
个数据。现在让我们用另一个实体扩展示例 - 'OrderLine(Id,OrderId,ProductId,Quantity)and
Product(Id,Name)`。现在我们想要查询客户的订单,订单行和产品:
var customer = context.Customers
.Include("Orders.OrderLines.Product")
.SingleOrDefault(c => c.Id == 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
正如您所看到的,数据变得非常复杂。通常每个包含参考导航属性(示例中为Product
)将添加新列,每个包含到集合导航属性(示例中为Orders
和OrderLines
)将添加新列并为包含的集合中的每一行复制已创建的行。
这意味着您的示例可以轻松拥有数百列和数千行,这是要传输的大量数据。正确的方法是创建性能测试,如果结果不能满足您的期望,您可以通过自己的查询或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);
此外,您应始终只加载您真正需要的数据。
编辑:我刚刚创建了proposal on Data UserVoice以支持其他热切加载策略,其中急切加载的数据将在其他结果集中传递(由同一数据库往返中的单独查询创建)。如果您发现这种改进很有意思,请不要忘记投票支持该提案。
答案 1 :(得分:15)
您可以通过以下数据库创建2个或更多小数据请求来提高许多包含的效果。
根据我的经验,只能给出每个查询最多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();
以上将通过使用更多旅行到数据库来从数据库中提取小数据集。
我使用自己的经验在上面写了一篇博文。这是Here
我希望这会对你有所帮助。
答案 2 :(得分:8)
是的,它会。如果扩展主表行上的多个详细信息行,请避免使用Include。
我相信EF会将查询转换为一个大型连接而不是多个查询。因此,您最终会在详细信息表的每一行上复制主表数据。
例如:Master - >细节。比如说,master有100行,Details有5000行(每个master有50行)。
如果您延迟加载详细信息,则返回100行(size:master)+ 5000行(size:details)。
如果使用.Include(“Details”),则返回5000行(size:master + details)。基本上,主要部分重复了50多次。
如果包含多个表,它会向上相乘。
检查EF生成的SQL。
答案 3 :(得分:3)
我建议您执行负载测试并测量压力下网站的性能。如果您对每个请求执行复杂查询,您可以考虑缓存一些结果。
答案 4 :(得分:2)
包含的结果可能会发生变化:它取决于调用include方法的实体。
就像Ladislav Mrnka提出的例子一样,假设我们有一个实体
客户(身份证,姓名,地址)
映射到此表:
Id | Name | Address
-----------------------
C1 | Paul | XYZ
和实体订单(Id,CustomerId,Total)
映射到此表:
Id | CustomerId | Total
-----------------------
O1 | C1 | 10.00
O2 | C1 | 13.00
关系一个客户与多个订单
示例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
示例2:订单=>客户强>
var order = context.Orders
.Include("Customers")
.SingleOrDefault(c => c.Id == "O1");
Linq将在简单的sql Join中翻译。
在这种情况下,查询将只生成一条没有重复信息的记录:
Order.Id | Order.Total | Customer.Id | Customer.Name
-----------------------------------------------------------
O1 | 10.00 | C1 | Paul