如何在 EF 4.1 RC 中的 DbContext 级别关闭更改跟踪?

发布于 2024-10-28 21:06:42 字数 1951 浏览 2 评论 0原文

我遇到了一个似乎很常见的问题:我正在更新数据库中的值,但 EF 正在使用对象的原始内存副本,并且这些更改的值不会反映在显示的数据中。我明白这是为什么,但我无法找到解决方法。

最常见的解决方案似乎是设置 MergeOptions.NoTracking 完全关闭更改跟踪(或在查询时使用 AsNoTracking() 扩展方法)并每次强制刷新对象被访问,这对于我的目的来说很好。

我有一个通用的基础存储库,我的其他存储库继承自:

public abstract class RepositoryBase<T> where T : class
{
    private readonly IDbSet<T> _dbset;
    private readonly IUnitOfWork _unitOfWork;

    protected RepositoryBase(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        _dbset = _unitOfWork.Database.Set<T>();
    }

    public virtual IQueryable<T> All()
    {
        return _dbset;
    }

    // Some other IQueryable methods here (Query, GetByProductCode etc)

    public virtual T Get(long id)
    {
        return _dbset.Find(id);
    }
}    

和一个像这样的 DbContext

public class Db : DbContext
{
    private IDbSet<Product> _products;

    public IDbSet<Product> Products
    {
        get { return _products ?? (_products = DbSet<Product>()); }
    }

    public virtual IDbSet<T> DbSet<T>() where T : class
    {
        return Set<T>();
    }

    public virtual void Commit()
    {
        base.SaveChanges();
    }
}

如果我更改存储库的 All() 方法:

public virtual IQueryable<T> All()
{
    return _dbset.AsNoTracking();
}

我得到期望的结果 - 当刷新显示产品的页面时,会反映数据库中的更新。但是,我无法在 Get() 方法中执行此操作,因为该扩展方法仅适用于 IQueryable

理想情况下,我想在 DbContext 级别关闭此功能,因为我永远不需要更改跟踪,但似乎没有明显的方法可以做到这一点,而且文档几乎为零关于这个主题(除非有人能给我指出一些?拜托!)。

我尝试在禁用这些配置选项的情况下向 DbContext 添加构造函数:

public Db()
{
    base.Configuration.ProxyCreationEnabled = false;
    base.Configuration.AutoDetectChangesEnabled = false;
}

但我必须承认我只是猜测它们的真正用途(我只是通过查看源代码找到了它们),并且无论如何,它们似乎没有任何效果。

任何帮助将不胜感激。如果更多信息/代码有帮助,请告诉我。

I've encountered what seems to be a common problem: I am updating values in my database, but EF is using its original in-memory copy of the object and these changed values are not reflected in the displayed data. I understand why this is, but I can't figure out a way around it.

The most common solution seems to be to set MergeOptions.NoTracking to turn off change tracking completely (or use the AsNoTracking() extension method when querying) and force a refresh every time the object is accessed, which is fine for my purposes.

I've got a generic base repository which my other repositories inherit from:

public abstract class RepositoryBase<T> where T : class
{
    private readonly IDbSet<T> _dbset;
    private readonly IUnitOfWork _unitOfWork;

    protected RepositoryBase(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        _dbset = _unitOfWork.Database.Set<T>();
    }

    public virtual IQueryable<T> All()
    {
        return _dbset;
    }

    // Some other IQueryable methods here (Query, GetByProductCode etc)

    public virtual T Get(long id)
    {
        return _dbset.Find(id);
    }
}    

And a DbContext like this:

public class Db : DbContext
{
    private IDbSet<Product> _products;

    public IDbSet<Product> Products
    {
        get { return _products ?? (_products = DbSet<Product>()); }
    }

    public virtual IDbSet<T> DbSet<T>() where T : class
    {
        return Set<T>();
    }

    public virtual void Commit()
    {
        base.SaveChanges();
    }
}

If I change the All() method of my repository thus:

public virtual IQueryable<T> All()
{
    return _dbset.AsNoTracking();
}

I get the desired result - an update in the database is reflected when the page displaying the products is refreshed. However, I can't do this in the Get() method, as that extension method only works on an IQueryable.

Ideally I'd like to turn this off at the DbContext level as I will never need change tracking, but there doesn't seem to be an obvious way to do this, and there is pretty much zero documentation on the subject (unless someone can point me to some? Please!).

I tried adding a constructor to the DbContext with these configuration options disabled:

public Db()
{
    base.Configuration.ProxyCreationEnabled = false;
    base.Configuration.AutoDetectChangesEnabled = false;
}

But I must admit I'm only guessing as to what they really do (I only found them through looking at the source code), and they don't seem to have any effect anyway.

Any help would be greatly appreciated. If more info/code would help, please let me know.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情魔剑神 2024-11-04 21:06:42

如果您想在每次不想使用 Find 方法时强制上下文获取新数据。 Find 方法总是首先查询内部存储。改用这个:

public virtual T Get(long id)
{
    return All().SingleOrDefault(e => e.Id == id);
}

但我不明白你需要这个做什么?你的意思是:

反映了数据库中的更新
当页面显示产品时
已刷新

上下文是工作单元。它应该用作工作单元 - 在 Web 应用程序或 Web 服务中,这意味着每个请求创建新的上下文实例。在 winforms / wpf 应用程序中,这意味着每个逻辑块(每个演示者等)使用上下文。因此,您应该仅在非常特定的情况下需要它,但您需要在全球范围内使用它。您的描述似乎是在请求之间重用上下文,这是完全糟糕的解决方案。为每个请求重新创建上下文不会产生性能成本。

If you want to force context to get fresh data each time you don't want to use Find method. Find method always query internal storage first. Use this instead:

public virtual T Get(long id)
{
    return All().SingleOrDefault(e => e.Id == id);
}

But I don't understand what do you need this? What do you mean by:

an update in the database is reflected
when the page displaying the products
is refreshed

Context is unit of work. It should be used as unit of work - in web application or web service it means creating new context instance per request. In winforms / wpf application it means using context per logical block (per presenter etc). Because of that you should need this only in very specific scenarios but you want it globally. Your description seems like you are reusing context among requests which is completely bad solution. There are no performance costs in recreating context for each request.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文