[英]Entity Framework doesn't update value which is modified by a trigger
我的表Sections
(SQL Server)具有ID
作為主鍵(int, identity)
和SortIndex
列(int)以進行排序。
數據庫有一個觸發器, SortIndex := ID
在每個INSERT
設置SortIndex := ID
。 顯然,我想稍后通過交換兩行的值來更改排序索引。
我使用Entity Framework訪問數據,所有這些都使用MVC3 Web應用程序。
問題是,在我將新對象插入表后,Entity Framework不會更新SortIndex
的值。 它還會緩存所有數據,因此以下調用從此表中獲取所有對象也會為此對象提供錯誤的SortIndex
值。
我嘗試在EDMX
為此列更改StoreGeneratedPattern
。 這似乎是偉大而優雅的,但並沒有解決問題。
如果我設置為Identity
,則會導致EF正確更新該值,但它將變為只讀(嘗試更改時拋出異常)。 將其設置為Computed
類似,但不是拋出異常,而是不將值寫入DB。
如果我需要在插入對象后使用它,我可以每次重新創建EF對象,只需執行以下操作:
DatabaseEntities db = new DatabaseEntities()
但對我來說這似乎是一種丑陋的解決方法。
這個問題的解決方案是什么?
顯然是一些東西,不需要我在每次insert
后做一些動作(並冒一個被遺忘和不被注意的風險)是首選。
簡而言之, StoreGeneratedPattern
意味着:該值由商店處理,您的應用程序永遠不會修改它。 在這種情況下,您將在調用SaveChanges
后自動獲得商店生成的值。
如果您不使用StoreGeneratedPattern
,則無法獲得價值,您必須強制執行另一個查詢以刷新您的實體。 你可以這樣做:
objectContext.Refresh(RefreshMode.StoreWins, yourSection);
通常,您需要通過觸發器和應用程序更新數據庫中的值的情況與EF(以及可能還有其他ORM工具)不能很好地協同工作。
我發現'Ladislav Mrnka'的答案是准確的並且標記為接受。 以下是我在嘗試尋找解決方案時發現的其他解決方法。 但是,我一直在尋找的解決方案通常是不可能的。
其中一種可能性是設置StoreGeneratedPattern = Computed
以讓EF知道,計算此值。 然后,創建一個存儲過程來實際更改SortIndex
的值。 通常,它會更改兩行中的值(交換它們),以更改排序順序。 此過程以及INSERT
的觸發器可確保數據在數據庫中保持一致。 如果沒有在SortIndex
設置適當的值,則無法創建新行,不可能使兩個對象具有相同的值(除非存儲過程有錯誤),並且無法以某種方式手動中斷該值,因為無法編輯通過EF。 看起來很棒的解決方案。
可以很容易地將存儲過程映射到EF中的函數。
問題是,現在可以輸入新行並且EF正確更新其緩存中的數據,但是在調用存儲過程后緩存不會更新。 仍然需要一些手動更新或刷新功能。 否則,以下調用以獲取按SortIndex
排序的SortIndex
將產生錯誤的結果。
除此之外,可以為多個實體設置MergeOption = MergeOption.OverwriteChanges
,這會使EF更好地更新數據庫中的數據。 完成此操作后,可以在插入或調用存儲過程后重新讀取對象,它將被刷新。 但是,使用db.Section.OrderBy(o => o.SortIndex)
讀取對象集合仍將返回錯誤排序順序的緩存結果。
如果有人感興趣,可以通過添加EF OnContextCreated
類和部分方法OnContextCreated
使MergeOption
默認為其他東西,如下所示:
public partial class DatabaseEntities
{
partial void OnContextCreated()
{
Subsection.MergeOption = MergeOption.OverwriteChanges;
Section.MergeOption = MergeOption.OverwriteChanges;
Function.MergeOption = MergeOption.OverwriteChanges;
}
}
我有一個與Sql Server Quote表類似的情況,其中varchar QuoteNumber列是一個非主要唯一鍵,其值由插入后觸發器生成。 使用觸發器是因為生成的值是通過從外鍵表中獲取數據而得到的。 Sql Server架構標識聲明不允許您從其他表中提取信息。
我希望EF將此varchar列視為標識,並在更新時對其執行任何操作,並在插入后重新讀取它。 如果.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)屬性在它生成的代碼中的非標識列中配置實體(如右圖所示),EF將執行此操作:
public QuoteConfiguration(string schema)
{
ToTable("Quote", schema);
HasKey(x => x.ID);
Property(x => x.ID).HasColumnName(@"ID").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.QuoteNumber).HasColumnName(@"Quote_Number").HasColumnType("varchar").IsOptional().IsUnicode(false).HasMaxLength(64).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
}
我的EF模型首先是代碼,由Simon Hughes的EntityFramework Reverse POCO Generator生成。 起初,我無法弄清楚如何使生成器將此屬性添加到未在Sql Server中聲明為標識的列。
插入后重讀整個Quote實體不會檢索自動生成的QuoteNumber。 然后我發現在插入后重新讀取QuoteNumber列擊敗了實體緩存。 但是,我覺得這樣做很臟。
最后,我與Simon Hughes一起探討如何讓他的EF Reverse POCO為我做這件事。 您只需在* .tt文件中擴展UpdateColumn函數,如下所示:
Settings.UpdateColumn = (Column column, Table table) =>
{
if (table.Name.Equals("Quote", StringComparison.InvariantCultureIgnoreCase)
&& column.Name.Equals("Quote_Number", StringComparison.InvariantCultureIgnoreCase))
{
column.IsStoreGenerated = true;
}
}
你知道你是否會在同一個請求中再次使用該列?
我將使用每個請求場景的上下文,這通常可以解決許多問題,因為每個請求都會創建一個新的EF上下文,因此每個請求都會有一個新數據。
對於長期存在的背景,如您所述,可能會出現緊張情緒。
無論如何,StoreGeneratedPattern設置為計算應該是正確的。 但它只在您存儲實際實體時才會更新。 它不是通過插入或更新任何其他實體來更新的。
來自http://msdn.microsoft.com/en-us/library/dd296755(v=vs.90).aspx
如果創建新實體或更改現有實體,則在應用程序中調用SaveChanges方法時,將從服務器檢索StoreGeneratedPattern設置為Computed的屬性值。 如果為應用程序中StoreGeneratedPattern設置為Computed的屬性分配值,則在調用SaveChanges方法時,將使用服務器生成的值覆蓋該值。
我們正在使用SQL序列GUID的計算值選項,它正常工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.