如何使用 Entity Framework 显式加载长导航属性链?
How to explicit loading long navigation properties' chain using Entity Framework?
我有这段代码可以显式加载实体:
dbContext.StorageRequests.Add(storageRequest);
dbContext.SaveChanges();
//Here I want to explict loading some navigation properties
dbContext.Entry(storageRequest).Reference(c => c.Manager).Load();
dbContext.Entry(storageRequest).Reference(c => c.Facility).Load();
dbContext.Entry(storageRequest).Collection(x=> x.PhysicalObjects).Query().Include(x => x.Classification).Load();
我的问题分为两部分:
第一个如何一起加载(我想调用 Load() 一次)?
上面的代码的第二部分是否为每个 Load() 调用发送查询,这又会访问数据库以加载相关数据?
我对 EF 核心有类似的问题。打开 SQL 记录到 debugoutput window 帮助回答了我的很多问题,关于它在做什么,为什么。针对您的问题:
1) 你不能,尽管你可以使用一系列 dbContext.Collection.Include(otherCollection).ThenInclude(stuffRelatedToOtherCollection)
类型链
预先加载它
2) 是的,即使在一个 c# 语句中预先加载也会引发多个查询。我认为这是因为除了最简单的倍数 sql 之外,它是一个太复杂的人工智能问题,无法以任何方式解决,因为当多个表连接在一起时,框架很难处理笛卡尔积一个矩形数据集。 (一个学校有学生和老师,teacher:students是many:many关系,分解为class。如果你写一个查询加入学校,class,学生和老师,你到处都是重复的数据,虽然从概念上讲可以通过它来寻找独特的学校,class 老师和学生的主键值,但您可能会下载数万个重复的行,只需要对它们进行唯一处理又一次。EF倾向于select学校,然后学校加入class,然后学校加入class加入学生,然后学校加入class加入教师(如果你是这样编码的您的学校包括 class 然后包括学生然后包括教师。更改您的包括策略将更改 运行)
的查询
好问题!让我用新的信息以相反的顺序回答不同的问题。
2.)
每个 Load()
都会导致对数据库的查询(Querying and Finding Entities - 10/23/2016):
A query is executed against the database when:
- It is enumerated by a foreach (C#) or For Each (Visual Basic) statement.
- It is enumerated by a collection operation such as ToArray, ToDictionary, or ToList.
- LINQ operators such as First or Any are specified in the outermost part of the query.
- The following methods are called: the Load extension method on a DbSet, DbEntityEntry.Reload, and Database.ExecuteSqlCommand.
人们经常使用eager loading和Include()
来让EF尽可能优化:
in most cases, EF will combine the joins when generating SQL
// ef 6
using System.Data.Entity;
var storageRequests = dbContext.StorageRequests
.Include(r => r.PhysicalObjects.Select(p => p.Classification))
.Include(r => r.Manager)
.Include(r => r.Facility);
// evaluate "storageRequests" here by linq method or foreach
或:
// ef core
var storageRequests = dbContext.StorageRequests
.Include(r => r.PhysicalObjects)
.ThenInclude(p => p.Classification)
.Include(r => r.Manager)
.Include(r => r.Facility);
// evaluate "storageRequests" here by linq method or foreach
1.)
我能想到的唯一可能的方法是将上述代码与 storageRequests.Load()
一起使用。
您可以检查它是否:
- 生成 single/multiple 个查询,
- 沿
StorageRequest
加载导航 属性 数据。
仅供参考:这些查询执行在微软文档中也称为 网络往返:
Multiple network roundtrips can degrade performance, especially where latency to the database is high (for example, cloud services).
兴趣点:
.Net Core 5 中有一个相对新的选项 Single vs. Split Queries (10/03/2019)。
默认为单个查询(上述行为)。之后,您可以决定 request/load 每个 table 数据,方法是在评估之前将 .AsSplitQuery()
添加到您的 linq 查询中。拆分查询会增加往返次数和内存使用量(不加载不同的数据)但有助于提高性能。
还有 .AsSingleQuery()
如果 your global choice 是:
.UseSqlServer(
connectionString,
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
我有这段代码可以显式加载实体:
dbContext.StorageRequests.Add(storageRequest);
dbContext.SaveChanges();
//Here I want to explict loading some navigation properties
dbContext.Entry(storageRequest).Reference(c => c.Manager).Load();
dbContext.Entry(storageRequest).Reference(c => c.Facility).Load();
dbContext.Entry(storageRequest).Collection(x=> x.PhysicalObjects).Query().Include(x => x.Classification).Load();
我的问题分为两部分:
第一个如何一起加载(我想调用 Load() 一次)?
上面的代码的第二部分是否为每个 Load() 调用发送查询,这又会访问数据库以加载相关数据?
我对 EF 核心有类似的问题。打开 SQL 记录到 debugoutput window 帮助回答了我的很多问题,关于它在做什么,为什么。针对您的问题:
1) 你不能,尽管你可以使用一系列 dbContext.Collection.Include(otherCollection).ThenInclude(stuffRelatedToOtherCollection)
类型链
2) 是的,即使在一个 c# 语句中预先加载也会引发多个查询。我认为这是因为除了最简单的倍数 sql 之外,它是一个太复杂的人工智能问题,无法以任何方式解决,因为当多个表连接在一起时,框架很难处理笛卡尔积一个矩形数据集。 (一个学校有学生和老师,teacher:students是many:many关系,分解为class。如果你写一个查询加入学校,class,学生和老师,你到处都是重复的数据,虽然从概念上讲可以通过它来寻找独特的学校,class 老师和学生的主键值,但您可能会下载数万个重复的行,只需要对它们进行唯一处理又一次。EF倾向于select学校,然后学校加入class,然后学校加入class加入学生,然后学校加入class加入教师(如果你是这样编码的您的学校包括 class 然后包括学生然后包括教师。更改您的包括策略将更改 运行)
的查询好问题!让我用新的信息以相反的顺序回答不同的问题。
2.)
每个 Load()
都会导致对数据库的查询(Querying and Finding Entities - 10/23/2016):
A query is executed against the database when:
- It is enumerated by a foreach (C#) or For Each (Visual Basic) statement.
- It is enumerated by a collection operation such as ToArray, ToDictionary, or ToList.
- LINQ operators such as First or Any are specified in the outermost part of the query.
- The following methods are called: the Load extension method on a DbSet, DbEntityEntry.Reload, and Database.ExecuteSqlCommand.
人们经常使用eager loading和Include()
来让EF尽可能优化:
in most cases, EF will combine the joins when generating SQL
// ef 6
using System.Data.Entity;
var storageRequests = dbContext.StorageRequests
.Include(r => r.PhysicalObjects.Select(p => p.Classification))
.Include(r => r.Manager)
.Include(r => r.Facility);
// evaluate "storageRequests" here by linq method or foreach
或:
// ef core
var storageRequests = dbContext.StorageRequests
.Include(r => r.PhysicalObjects)
.ThenInclude(p => p.Classification)
.Include(r => r.Manager)
.Include(r => r.Facility);
// evaluate "storageRequests" here by linq method or foreach
1.)
我能想到的唯一可能的方法是将上述代码与 storageRequests.Load()
一起使用。
您可以检查它是否:
- 生成 single/multiple 个查询,
- 沿
StorageRequest
加载导航 属性 数据。
仅供参考:这些查询执行在微软文档中也称为 网络往返:
Multiple network roundtrips can degrade performance, especially where latency to the database is high (for example, cloud services).
兴趣点:
.Net Core 5 中有一个相对新的选项 Single vs. Split Queries (10/03/2019)。
默认为单个查询(上述行为)。之后,您可以决定 request/load 每个 table 数据,方法是在评估之前将 .AsSplitQuery()
添加到您的 linq 查询中。拆分查询会增加往返次数和内存使用量(不加载不同的数据)但有助于提高性能。
还有 .AsSingleQuery()
如果 your global choice 是:
.UseSqlServer(
connectionString,
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));