我遇到了似乎常见的问题:我正在更新数据库中的值,但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;
}
但我必须承认,我只是猜测他们真正做了什么(我只是通过查看源代码才发现它们),而且它们似乎没有任何影响。
非常感谢任何帮助。如果有更多信息/代码可以提供帮助,请告知我们。
答案 0 :(得分:6)
如果您希望每次不想使用Find
方法时强制上下文获取新数据。 Find
方法始终首先查询内部存储。请改用:
public virtual T Get(long id)
{
return All().SingleOrDefault(e => e.Id == id);
}
但我不明白你需要什么?你是什么意思:
反映了数据库中的更新 当页面显示产品时 刷新
背景是工作单元。它应该用作工作单元 - 在Web应用程序或Web服务中,它意味着为每个请求创建新的上下文实例。在winforms / wpf应用程序中,它意味着使用每个逻辑块的上下文(每个演示者等)。因此,您应该仅在非常特定的情况下才需要它,但您需要全局。您的描述似乎是在completely bad solution请求之间重用上下文。在为每个请求重新创建上下文时没有性能成本。