2013-04-05 345 views
39

我是實體框架的新手。實體框架緩存問題

我已經在我的數據庫中使用EF得到了一些值。它完美地返回,並且值顯示在標籤中。但是,當我刪除我的表中的所有值(不使用EF),EF查詢返回我的舊值。我知道EF將這些值存儲在緩存中,併爲隨後的運行返回緩存的數據。它是否正確?

那麼我怎樣才能解決問題,當我刪除我的數據庫中的所有值,但EF返回舊值?

編輯

現在我使用datamodel.SaveChanges()。但現在它返回相同的舊值。

我的示例查詢看起來象下面這樣:除非你重新查詢上下文

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities(); 
datamodel.SaveChanges(); 
List<Compliance> compliance=new List<Compliance>(); 
IList<ComplianceModel> complianceModel; 
if (HttpContext.Current.User.IsInRole("SuperAdmin")) 
{ 
    compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList(); 
} 
+3

您是否在您的dbContext上調用了'.SaveChanges();'如果你沒有這些實體將不會被刪除.. – Rob 2013-04-05 08:04:09

+0

請閱讀我的完整問題。我沒有在代碼中使用刪除。我直接刪除了數據庫。數據庫 – 2013-04-05 08:05:49

+0

您的上下文如何?當你需要時打開上下文,一旦完成就關閉它。 (您可能想要改進您的問題,但一點也不清楚) – 2013-04-05 08:07:18

回答

25

如果您知道變化發生EF之外,並希望刷新您的特定實體ctxt,您可以撥打ObjectContext.Refresh

datamodel.Refresh(RefreshMode.StoreWins, orders); 

如果這看起來這將是一個普遍的現象,你應該在查詢中禁用對象緩存:

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities(); 
datamodel.tblCities.MergeOption = MergeOption.NoTracking; 

或關閉對象級緩存爲特定的實體,

Context.Set<Compliances>().AsNoTracking(); 
+3

.AsNoTracking()對我來說 – nadav 2016-09-23 13:03:36

+1

如果使用AsNoTracking(),那麼DBContext將不會跟蹤對Enetities的更改。在這種情況下,您將無法使用dbContext.SaveChanges()修改實體並保存它們。 – 2017-05-03 19:08:22

+1

http://codethug.com/2016/02/19/Entity-Framework-Cache-Busting/ – 2017-05-03 19:11:15

20

EF不會加載的變化。 EF查詢數據庫和加載將它們映射到對象中,它監視您在對象上執行的更改,而不是數據庫上執行的更改。 EF不會跟蹤直接對數據庫所做的更改,並且它不會跟蹤。

你已經加載了一個List,List是你在內存中的緩存。即使調用保存更改也不會刷新。您將不得不再次查詢上下文,即創建新列表。

要看到改變,你必須執行以下行一次,

datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList() 
25

當你每方面使用EF默認情況下它負載的每一個實體只有一次。 第一個查詢創建實體實例並將其存儲在內部。隨後的查詢要求具有相同密鑰的實體返回此 存儲的實例。如果數據存儲值改變您仍然收到 的實體值從初始查詢

一個謹慎的回答:

https://stackoverflow.com/a/3653392/1863179

+8

短版:「永遠不要使用全局上下文」 – Colin 2015-05-12 13:38:36

+1

我一直在調試像瘋了一樣,進入SSMS並檢查我能找到的所有東西爲什麼某些電話沒有被更新......現在我知道了!謝謝! – RemarkLima 2016-05-19 20:03:47

4

下面的代碼幫助我的對象,與新鮮的數據庫值被刷新。該條目(對象).Reload()命令強制對象召回數據庫值

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName); 
DatabaseObjectContext.Entry(member).Reload(); 
4

,我建議你使用一些MergeOption所有EntitieSet創建上下文後,像這樣:

var objSetProps = ctx.GetType().GetProperties().Where(prop => prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(ObjectSet<>)); 
foreach (PropertyInfo objSetProp in objSetProps) 
{ 
    ObjectQuery objSet = (ObjectQuery)objSetProp.GetValue(ctx, BindingFlags.GetProperty, null, null, null); 
    objSet.MergeOption = MergeOption.PreserveChanges; 
} 

閱讀關於MergeOption在這裏:http://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption.aspx 你會使用NoTracking,我想。

但是,如果您清除了「緩存」的實體,則將其分開。

var entidades = Ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged); 
foreach (var objectStateEntry in entidades) 
    Ctx.Detach(objectStateEntry.Entity); 

其中Ctx是我的上下文。

2

首先,我不會建議修改數據庫外部的系統,除非你只做測試和開發。

EF DbContext包含一個IDisposable接口。要釋放任何緩存數據,請手動創建Dispose調用或將數據庫對象放入使用塊中。

 using (SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities()) 
     { 
      List<Compliance> compliance = new List<Compliance>(); 
      IList<ComplianceModel> complianceModel; 
      if (HttpContext.Current.User.IsInRole("SuperAdmin")) 
      { 
       compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList(); 
      } 
     } 

這將確保上下文在下次使用時被清除並重新創建。請務必爲您的所有來電執行此操作,而不僅僅是您遇到的問題。

2

我懷疑這裏的根本問題是你的DbContext掛了太久。我可以從你正在使用HttpContext,你有一個Web應用程序的事實告訴,並General guidelines when working with DbContext包括

當使用Web應用程序的工作,每 要求使用上下文實例。

如果您正在使用MVC,你可以使用Dispose模式在你的控制器是這樣的:

public class EmployeeController : Controller 
{ 
    private EmployeeContext _context; 

    public EmployeeController() 
    { 
     _context = new EmployeeContext(); 
    } 

    public ActionResult Index() 
    { 
     return View(_context.Employees.ToList()); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      _context.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 
} 

但你真的應該尋找在依賴注入manage the DbContext lifetime

0

夫婦的事情,你可以做。

  1. 使用新的上下文。緩存的實體存儲在上下文中。使用新的上下文會阻止它使用緩存。
  2. 如果你真的想要一個全局/持久的上下文,你有兩個子選項: a。)總是調用Reload方法。 db.Entry(entity).Reload()...這會強制上下文重新加載該實體。 b。)使用SqlDependency對象來檢測記錄何時根據需要更改和重新加載實體。 https://code.msdn.microsoft.com/How-to-use-SqlDependency-5c0da0b3
2

我覺得你需要的是GetDatabaseValues()。它用於這樣的:下面

context.Entry(/*your entry*/).GetDatabaseValues(); 

信息是從msdn

的電流值是該實體 的性質目前包含的值。原始值是查詢實體時從數據庫中讀取的值 。數據庫值爲 當前存儲在數據庫中的值。當數據庫中的值可能有 由於查詢實體而發生更改,例如當數據庫由另一個用戶併發編輯到 時,獲取 數據庫值很有用。

7

我認爲你應該遵循這裏的一些其他解決方案,但它似乎是你想清除緩存。您可以通過執行以下操作來實現此目的:

var count = datamodel.Compliances.Local.Count; // number of items in cache (ex. 30) 

datamodel.Compliances.Local.ToList().ForEach(c => { 
    datamodel.Entry(c).State = EntityState.Detached; 
}); 

count = datamodel.Compliances.Local.Count; // 0 
+0

這是解決問題的最佳解決方案。只需清除DbContext的緩存,就可以開始了。 – 2017-06-20 05:22:56