同一個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; }