8

我遇到了一个似乎很常见的问题:我正在更新我的数据库中的值,但 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;
}

但我必须承认,我只是在猜测它们的真正作用(我只是通过查看源代码才发现它们),而且它们似乎没有任何作用。

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

4

1 回答 1

6

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

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

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

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

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

于 2011-03-31T16:12:59.803 回答