EF Core 在添加现有查询时附加所有实体
EF Core 在添加现有查询时附加所有实体
我有一些复杂的场景,但我会在这里简化展示。所以,我的上下文类非常简单:\n
public class AppDbContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("data source=DILSHODKPC;integrated security=True;Database=ODataTEst; MultipleActiveResultSets=true"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { var student = modelBuilder.Entity(); student.HasKey(s => s.Id); } public IQueryable GetEntities () where T : class { return Set (); } }
\n我的模型也很简单:\n
public class Student { public Guid Id { get; set; } public string Name { get; set; } public int Score { get; set; } }
\n数据库中有8条记录:\n\n我的问题是当我向现有的实体框架中添加查询时,它会跟踪所有实体。看两种情况:\n情景1:\n
AppDbContext appDbContext = new AppDbContext(); var query1 = appDbContext.GetEntities().Where(s => s.Score > 3); var query2 = query1.Where(s => s.Name == "Messi"); var loadedEntitiesCount = query2.ToList().Count();//Count is 4. It is good var trackedEntitiesCount = appDbContext.ChangeTracker.Entries ().Select(e => e.Entity).Count();//Count is 4. It is good
\n在这种情况下,一切都是预期的。\n情景2:\n
static void Main(string[] args) { AppDbContext appDbContext = new AppDbContext(); var query1 = GetFirstQuery(appDbContext); var query2 = query1.Where(s => s.Name == "Messi"); var loadedEntitiesCount = query2.ToList().Count();//Count is 4. It is good var trackedEntitiesCount = appDbContext.ChangeTracker.Entries().Select(e => e.Entity).Count();//Count is 8. Confusing } static IEnumerable GetFirstQuery(AppDbContext appDbContext) { return appDbContext.GetEntities ().Where(s => s.Score > 3); }
\n在这种情况下,跟踪实体的数量为8。我没有预料到这个结果。\n情景3:\n
AppDbContext appDbContext = new AppDbContext(); var query1 = GetFirstQuery(appDbContext).AsQueryable(); var query2 = query1.Where(s => s.Name == "Messi"); var loadedEntitiesCount = query2.ToList().Count();//Count is 4. It is good var trackedEntitiesCount = appDbContext.ChangeTracker.Entries().Select(e => e.Entity).Count();//Count is 4. It is good
\n跟踪实体的数量为4。\n问题是为什么在情景2中ef会跟踪所有实体?这是否是预期的行为?在现有查询上添加不同类型的集合(IEnumerable,IQueryable)的最佳实践是什么?
EF Core在添加查询到现有查询时附加所有实体的原因是因为返回类型为IEnumerable<T>的方法会将查询结果加载到内存中,而不会将查询继续传递到数据库中执行。解决方法是将返回类型改为IQueryable<T>,以便将查询继续传递到数据库中执行。
一般来说,如果你不希望跟踪对象,可以在查询中调用.AsNoTracking()
。这样,查询的结果将不会被跟踪。
在你的特定情况下:
static IEnumerable<Student> GetFirstQuery(AppDbContext appDbContext)
这意味着查询在此结束,因为你返回了一个IEnumerable。然后,查询结果将在应用程序的内存中进一步进行筛选,所以你会得到正确的结果,但所有的数据都已经从数据库中查询出来。
如果你希望查询继续进行并将进一步的筛选或表达式传递到数据库而不是在应用程序的内存中执行,则使用IQueryable<>:
static IQueryable<Student> GetFirstQuery(AppDbContext appDbContext)
“查询在此结束”是什么意思?在我调用ToList之前,它不会加载到内存中。GetFirstQuery的返回类型是IEnumerable<Student>,但返回值'instance'(实际)类型是IQuerable<Student>,将IEnumerable<Student>更改为IQueryable<Student>将不适用于我。
说实话,我对于适合你的情况无能为力。我可以告诉你编译器会做什么,这也是我刚刚所做的。你甚至有提供我说的话的证据的代码。一旦你通过任何方式将IQueryable转换为IEnumerable,之后你就在内存中操作了。
nvoigt是说你枚举了GetFirstQuery返回的可枚举对象,导致第一个查询appDbContext.GetEntities<Student>().Where(s => s.Score > 3)
完全运行(所有8个实体都与此where子句匹配),然后你在本地使用.Where(s => s.Name == "Messi");
进行了过滤。
Caius是正确的。我不是在谈论延迟执行,你是对的,在ToList调用处会发生延迟执行。我在谈论的是你的链中的部分是处理IQueryable的。这些部分会被转换为SQL并在数据库中处理。而那些部分是IEnumerable,它们在内存中进行。只要你希望链的继续操作也在数据库中执行,就需要保持它为IQueryable。
实质上,如果你使用返回IEnumerable的GetFirstQuery,就像这样做:appDbContext.GetEntities<Student>().Where(s => s.Score > 3).AsEnumerable().Where(s => s.Name == "Messi");
- 第二个Where枚举了可枚举对象,触发第一个查询的运行;这两个Where不会像通常那样进行复合操作。