EF Core 在对现有查询添加查询时附加所有实体 [重复]

Posted

技术标签:

【中文标题】EF Core 在对现有查询添加查询时附加所有实体 [重复]【英文标题】:EF Core attaching all entities while adding query on existing query [duplicate] 【发布时间】:2022-01-13 08:49:06 【问题描述】:

我有一些复杂的场景,但我会在这里简单地展示它们。所以,我的上下文类非常简单:

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>();
        student.HasKey(s => s.Id);
    

    public IQueryable<T> GetEntities<T>() where T : class
    
        return Set<T>();
    

我的模型也很简单:

public class Student

    public Guid Id  get; set; 

    public string Name  get; set; 

    public int Score  get; set; 

我在数据库中有 8 条记录:

我的问题是当我向现有实体框架添加查询时正在跟踪所有实体。看两个场景:

场景 1:

AppDbContext appDbContext = new AppDbContext();
var query1 = appDbContext.GetEntities<Student>().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<Student>().Select(e => e.Entity).Count();//Count is 4. It is good

在这种情况下,一切都在意料之中。

场景 2:

 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<Student>().Select(e => e.Entity).Count();//Count is 8. Confusing
        

        static IEnumerable<Student> GetFirstQuery(AppDbContext appDbContext)
        
            return appDbContext.GetEntities<Student>().Where(s => s.Score > 3);
        

在这种情况下,跟踪的实体数为 8。我没想到

场景 3:

   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<Student>().Select(e => e.Entity).Count();//Count is 4. It is good

跟踪的实体数为 4。

问题是为什么 ef 跟踪场景 2 中的所有实体?这是预期的吗?在具有不同类型集合的现有查询上添加查询的最佳实践是什么? (IEnumerable, IQueryable)

【问题讨论】:

【参考方案1】:

一般来说,如果您不想跟踪对象,您可以在查询时调用.AsNoTracking()。那么这个查询的结果就不会被跟踪了。

在您的具体情况下:

static IEnumerable<Student> GetFirstQuery(AppDbContext appDbContext)

这意味着查询在此处结束,因为您返回一个IEnumerable。然后结果将在您的应用程序的内存中进一步过滤,因此您得到正确的结果,但所有数据都将从数据库中查询。

如果您希望查询继续进行并将进一步的过滤器或表达式传输到数据库而不是在应用程序的内存中执行,请使用 IQueryable&lt;&gt;

static IQueryable<Student> GetFirstQuery(AppDbContext appDbContext)

【讨论】:

“查询到此结束”是什么意思?在我调用 ToList 之前,它不会加载到内存中。 GetFirstQuery 返回类型为 IEnumerable,但返回值“实例”(实际)类型为 IQuerable,将 IEnumerable 更改为 IQueryable 不适合我 老实说,我不能做很多适合你的事情。我可以告诉你编译器做了什么,我刚刚做了。你甚至有提供我所说的证据的代码。一旦你通过任何方式从 IQueryable 中创建了一个 IEnumerable,之后你就在内存中进行操作。 nvoigt 是说您枚举了 GetFirstQuery 返回的可枚举,导致第一个查询 appDbContext.GetEntities&lt;Student&gt;().Where(s =&gt; s.Score &gt; 3) 完整运行(并且所有 8 个实体都由该 where 子句匹配),然后您在本地将其过滤为梅西.Where(s =&gt; s.Name == "Messi"); Caius 是正确的。我不是在谈论延迟执行,你是对的,它发生在你的 ToList 调用中。我说的是你的链的一部分正在处理 IQueryable。这些部分被转换成 SQL 并在数据库上处理。部分在 IEnumerable 上,这些发生在内存中。只要您希望链的延续也能对数据库进行操作,您就需要将其保留为 IQueryable。 本质上,如果您使用 GetFirstQuery 返回 IEnumerable 执行此操作,就像在执行 appDbContext.GetEntities&lt;Student&gt;().Where(s =&gt; s.Score &gt; 3).AsEnumerable().Where(s =&gt; s.Name == "Messi"); - 第二个 Where 枚举可枚举,触发第一个查询运行;这两个 Where 不像他们通常那样在复合中行动

以上是关于EF Core 在对现有查询添加查询时附加所有实体 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

EF Core 5.0 - 更改“定义查询”映射实体时是不是需要生成迁移?

EF Core性能优化

LINQ 实体框架查询在 EF Core 中不起作用,引发异常

DTO对象

EF Core 上下文不包含添加实体的更改

Swift & Core Data - 将实体添加到现有实体