同一個DbContext實例,EF會對查詢過的數據進行緩存。對DbContext的生存期的管理需根據當前的應用來處理(如web、win),盡量不要采用全局的DbContext。也可在查詢中采用AsNoTracking避免從緩存中取數據。
1、問題
構建一個全局的或某個業務場景內唯一的DbContext
public OTBll()
{
efContext = new AppDbContext();
this.OT_DataFlowRepository = new EFRepository<OT_DataFlow>(efContext);
在此場景內間隔查詢數據
HR_SignSealPdf entity =context.HR_SignSealPdf.FirstOrDefault(e => e.SignId == item.SignId && e.Cate == item.Cate); entity.PdfUploadErrs += 1;
SSMS修改PdfUploadErrs后,仍會從entity.PdfUploadErrs的值上累加
UPDATE HR_SignSealPdf SET PdfUploadErrs=0
2、解決辦法
(1)每次構建一個新的DbContext實例,使用完畢就釋放
using (var context = new AppDbContext())
{
...
}
(2)AsNoTracking返回新的查詢實體
efContext.HR_SignSealPdf.AsNoTracking()
反編譯的代碼
/// <summary>返回一個新查詢,其中返回的實體將不會在 <see cref="T:System.Data.Entity.DbContext" /> 或 <see cref="T:System.Data.Entity.Core.Objects.ObjectContext" /> 中進行緩存。此方法通過調用基礎查詢對象的 AsNoTracking 方法來運行。如果基礎查詢對象沒有 AsNoTracking 方法,則調用此方法將不會有任何影響。</summary>
/// <returns>應用 NoTracking 的新查詢,如果不支持 NoTracking,則為源查詢。</returns>
/// <param name="source">源查詢。</param>
public static IQueryable AsNoTracking(this IQueryable source)
{
System.Data.Entity.Utilities.Check.NotNull<IQueryable>(source, nameof (source));
DbQuery dbQuery = source as DbQuery;
if (dbQuery == null)
return QueryableExtensions.CommonAsNoTracking<IQueryable>(source);
return (IQueryable) dbQuery.AsNoTracking();
}
3、優化
(1)web端可考慮在當前操作線程內定義個唯一的DbContext,以提高效率
//返回當前線程內的數據庫上下文,如果當前線程內沒有上下文,那么創建一個上下文,並保證上線問實例在線程內部是唯一的
public static VpoEntities GetCurrentDemoContext()
{
//CallContext:是線程內部唯一的獨用的數據槽(一塊內存空間)
//傳遞DbContext進去獲取實例的信息,在這里進行強制轉換。
VpoEntities dbContext = CallContext.GetData("DbContext") as VpoEntities;
if (dbContext == null) //線程在數據槽里面沒有此上下文
{
dbContext = new VpoEntities(); //如果不存在上下文的話,創建一個EF上下文
var adapter = (IObjectContextAdapter)dbContext;
var objectContext = adapter.ObjectContext;
objectContext.CommandTimeout = 1800;
//我們在創建一個,放到數據槽中去
CallContext.SetData("DbContext", dbContext);
}
return dbContext;
}
