我們有一個相當複雜的實體模型的應用程序,其中高性能和低延遲是必不可少的,但我們不需要橫向可伸縮性。除了自託管的ASP.NET Web API 2之外,該應用程序還有許多事件源。我們使用Entity Framework 6將POCO類映射到數據庫(我們使用優秀的Reverse POCO Generator來生成我們的類)。實體框架 - 如何緩存和共享只讀對象
每當事件到達時,應用程序必須對實體模型進行一些調整,並通過EF持續對數據庫進行增量調整。同時,讀取或更新請求可能通過Web API到達。因爲該模型涉及許多表和FK關係,並且對事件作出反應通常需要加載該實體下的所有關係,所以我們選擇將整個數據集維護在內存中緩存而不是加載每個事件的整個對象圖。下圖顯示了我們的模型的簡化版本: -
在程序啓動時,我們通過臨時DbContext
加載所有有趣的ClassA
實例(及其關聯依賴圖),並插入到一個詞典(即我們的緩存)。當事件到達時,我們在緩存中找到ClassA實例,並通過DbSet.Attach()
將其附加到每個事件DbContext
。該程序使用await-async模式編寫,並且可以同時處理多個事件。我們通過使用鎖來保護緩存的對象不被同時訪問,所以我們保證緩存的對象只能一次加載到DbContext
中。迄今爲止,表現非常出色,我們對該機制感到滿意。 但是有一個問題。雖然實體圖在ClassA
下相當獨立,但有些POCO類表示我們認爲是隻讀靜態數據(圖像中以橙色陰影)。我們發現EF有時會抱怨
IEntityChangeTracker的多個實例無法引用實體對象。
,當我們試圖Attach()
兩個不同的ClassA
情況下在同一時間(即使我們連接到不同的Dbcontexts
),因爲它們共享相同的ClassAType
的參考。
ConcurrentDictionary<int,ClassA> theCache = null;
using(var ctx = new MyDbContext())
{
var classAs = ctx.ClassAs
.Include(a => a.ClassAType)
.ToList();
theCache = new ConcurrentDictionary<int,ClassA>(classAs.ToDictionary(a => a.ID));
}
// take 2 different instances of ClassA that refer to the same ClassAType
// and load them into separate DbContexts
var ctx1 = new MyDbContext();
ctx1.ClassAs.Attach(theCache[1]);
var ctx2 = new MyDbContext();
ctx2.ClassAs.Attach(theCache[2]); // exception thrown here
有什麼辦法告知EF是ClassAType
只讀/靜態的,我們不希望它確保每個實例可以加載到只有一個 - :這是由下面的代碼片段演示DbContext
?到目前爲止,解決問題的唯一方法是修改POCO生成器以忽略這些FK關係,因此它們不是實體模型的一部分。但是這會使編程複雜化,因爲需要訪問靜態數據的處理方法有ClassA
。
嗨@Diana。我不認爲這有助於幾個原因。 1)如果我使用'AsNoTracking',那麼這些對象將不會被'DbContext'緩存,所以'ClassA'圖的後續加載將需要再次包含它們。 2)我相信實體的跟蹤狀態是DbContext的屬性,而不是對象本身,所以當原始DbContext被放置時,不會有跟蹤/不跟蹤的剩餘內存。 – Rob
你是對的,實體將從'DbContext'中分離出來,那麼它們就不會有'Entry'。然後我想你的解決方案將爲每個不同的'ClassA'圖創建這些實體的新實例,而不是使用相同的實例。就像在將它們分配給圖之前克隆它們一樣。 – Diana
我相信沿着這些路線的東西可以工作,但我認爲這將是加載實體圖的代碼的一個重大複雜化(它將不得不迭代所有的ClassA並替換隻讀的類實例 - 這些實例非常深入結構)。我希望在靜態數據類的層次上有更多的聲明。 – Rob