首页 > 解决方案 > 可以使用 .ToList() 绕过 DbContext 跟踪吗?

问题描述

我想DbContext尽可能缩短我的寿命,所以我利用了这个using声明。

但是,由于实体框架跟踪实体,我不能做这样的事情:

public IEnumerable<Person> GetPersons()
{
    using (var db = new AppContext())
    {
        Logging.Log(Information, "Requested getPersons from service");
        return db.Persons;
    }
}

因为只要我Persons在 ViewModel 中使用列表,我就会收到一个 InvalidOperationException(在我使用对象之前上下文已经被释放)。

为了绕过它,我通过调用将查询结果转换为具体的列表ToList()

public IEnumerable<Person> GetPersons()
{
    using (var db = new AppContext())
    {
        Logging.Log(Information, "Requested getPersons from service");
        return db.Persons.ToList();
    }
}

这是一件好事吗?AsNoTracking()没有任何影响,因为如果上下文被释放,它仍然返回一些被丢弃的实体。

标签: c#entity-framework

解决方案


如果您只是将db.Persons其返回IEnumerable<Person>给某个调用者,然后在该调用者可以迭代结果之前处理数据库上下文,您将得到异常,因为实体框架默认具有惰性。 ToList通过在释放上下文之前立即强制迭代来解决这个问题,但仍然有对象跟踪的开销,因此结合ToList使用AsNoTracking可以获得最佳性能。

AsNoTracking删除了数据库上下文中的一些内部跟踪代码,它不允许您IEnumerable<Person>在处理上下文后传递出去,您仍然需要这样做ToList

使用扩展方法AsNoTracking,然后调用ToList

db.Persons.AsNoTracking().ToList();

或者,您可以在 db 上下文的 change tracker 属性上禁用更改跟踪,以使整个内容只读,从而避免AsNoTracking到处调用。你仍然需要ToList这种方法。

/// <summary>
/// Disable all change tracking - place in db context sub class
/// </summary>
public void DisableChangeTracking()
{
    ChangeTracker.AutoDetectChangesEnabled = false;
    ChangeTracker.LazyLoadingEnabled = false;
    ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}

推荐阅读