【问题标题】:EF Core attaching all entities while adding query on existing query [duplicate]EF Core 在对现有查询添加查询时附加所有实体 [重复]
【发布时间】: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)

【问题讨论】:

    标签: c# entity-framework-core 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 不像他们通常那样在复合中行动
    猜你喜欢
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 2020-01-20
    • 1970-01-01
    相关资源
    最近更新 更多