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

发布于 2024-10-29 05:05:16 字数 773 浏览 2 评论 0 原文

我正在为我的个人资料页面使用以下 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。目前运行完美,但是当网站用户较多时,性能会不会受到很大影响?

我应该用其他方式来做吗?

I am using the following LINQ query for my profile page:

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;

It has a long object graph and uses many Includes. It is running perfect right now, but when the site has many users, will it impact performance much?

Should I do it in some other way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

蓝天白云 2024-11-05 05:05:16

包含包含的查询返回单个结果集,包含的数量会影响大数据集从数据库服务器传输到 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.

这意味着每个 Order 都会重复 Cutomers 数据。现在让我们使用另一个实体扩展该示例 -“OrderLine (Id, OrderId, ProductId, Quantity)andProduct (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)的包含都会添加新列,并且每个包含到集合导航属性(OrdersOrderLines > 在示例中)将为所包含集合中的每一行添加新列并复制已创建的行。

这意味着您的示例可以轻松拥有数百列和数千行,这是需要传输的大量数据。正确的方法是创建性能测试,如果结果不能满足您的期望,您可以通过自己的查询或通过 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);

此外,您应该始终仅加载真正需要的数据。

编辑:我刚刚创建了关于 Data UserVoice 的提案,支持额外的急切加载策略,其中急切加载的数据将在附加结果集中传递(由同一数据库往返中的单独查询创建)。如果您发现此改进很有趣,请不要忘记投票支持该提案。

A query with includes returns a single result set and the number of includes affect how big data set is transfered from the database server to the web server. Example:

Suppose we have an entity Customer (Id, Name, Address) and an entity Order (Id, CustomerId, Date). Now we want to query a customer with her orders:

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

The resulting data set will have the following structure:

 Id | Name | Address | OrderId | CustomerId | Date 
---------------------------------------------------
  1 |  A   |   XYZ   |    1    |     1      | 1.1.
  1 |  A   |   XYZ   |    2    |     1      | 2.1.

It means that Cutomers data are repeated for each Order. Now lets extend the example with another entities - 'OrderLine (Id, OrderId, ProductId, Quantity)andProduct (Id, Name)`. Now we want to query a customer with her orders, order lines and products:

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

The resulting data set will have the following structure:

 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

As you can see data become quite a lot duplicated. Generaly each include to a reference navigation propery (Product in the example) will add new columns and each include to a collection navigation property (Orders and OrderLines in the example) will add new columns and duplicate already created rows for each row in the included collection.

It means that your example can easily have hundreds of columns and thousands of rows which is a lot of data to transfer. The correct approach is creating performance tests and if the result will not satisfy your expectations, you can modify your query and load navigation properties separately by their own queries or by LoadProperty method.

Example of separate queries:

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();

Example of LoadProperty:

var customer = context.Customers
                      .SingleOrDefault(c => c.Id == 1);
context.LoadProperty(customer, c => c.Orders);

Also you should always load only data you really need.

Edit: I just created proposal on Data UserVoice to support additional eager loading strategy where eager loaded data would be passed in additional result set (created by separate query within the same database roundtrip). If you find this improvement interesting don't forget to vote for the proposal.

¢蛋碎的人ぎ生 2024-11-05 05:05:16

(您可以通过从数据库创建 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();

上面将通过使用更多的数据库访问来从数据库中带来小数据集。

(You can improve performance of many includes by creating 2 or more small data request from data base like below.

According to my experience,Only can give maximum 2 includes per query like below.More than that will give really bad performance.

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();

Above will bring small data set from database by using more travels to the database.

是你 2024-11-05 05:05:16

是的,会的。如果使用 Include 扩展主表行上的多个详细信息行,请避免使用 Include。

我相信 EF 会将查询转换为一个大型联接而不是多个查询。因此,您最终将在详细信息表的每一行上复制主表数据。

例如:大师->细节。假设 master 有 100 行,Details 有 5000 行(每个 master 50 行)。

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

如果您使用 .Include("Details"),您将返回 5000 行(大小:主数据 + 详细信息)。本质上,主部分被复制了 50 多次。

如果包含多个表,它会向上增加。

检查EF生成的SQL。

Yes it will. Avoid using Include if it expands multiple detail rows on a master table row.

I believe EF converts the query into one large join instead of several queries. Therefore, you'll end up duplicating your master table data over every row of the details table.

For example: Master -> Details. Say, master has 100 rows, Details has 5000 rows (50 for each master).

If you lazy-load the details, you return 100 rows (size: master) + 5000 rows (size: details).

If you use .Include("Details"), you return 5000 rows (size: master + details). Essentially, the master portion is duplicated over 50 times.

It multiplies upwards if you include multiple tables.

Check the SQL generated by EF.

哆兒滾 2024-11-05 05:05:16

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

I would recommend you to perform load tests and measure the performance of the site under stress. If you are performing complex queries on each request you may consider caching some results.

冰雪之触 2024-11-05 05:05:16

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

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

实体Customer(Id、Name、Address)

映射到的 此表:

Id  |  Name   | Address
-----------------------
C1  |  Paul   |   XYZ   

实体订单(Id、CustomerId、Total)

以及映射到此表的

Id |  CustomerId  | Total
-----------------------
O1 |      C1      |  10.00
O2 |      C1      |  13.00

:关系是一个客户许多订单


示例 1:Customer = >订单

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:订单 => Customer

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    

The result of include may change: it depend by the entity that call the include method.

Like the example proposed from Ladislav Mrnka, suppose that we have an entity

Customer (Id, Name, Address)

that map to this table:

Id  |  Name   | Address
-----------------------
C1  |  Paul   |   XYZ   

and an entity Order (Id, CustomerId, Total)

that map to this table:

Id |  CustomerId  | Total
-----------------------
O1 |      C1      |  10.00
O2 |      C1      |  13.00

The relation is one Customer to many Orders


Esample 1: Customer => Orders

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

Linq will be translated in a very complex sql query.

In this case the query will produce two record and the informations about the customer will be replicated.

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

Esample 2: Order => Customer

var order = context.Orders
                      .Include("Customers")
                      .SingleOrDefault(c => c.Id == "O1");

Linq will be translated in a simple sql Join.

In this case the query will produce only one record with no duplication of informations:

 Order.Id |  Order.Total |  Customer.Id   |   Customer.Name
-----------------------------------------------------------
     O1   |    10.00     |      C1        |       Paul    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文