[英]Implementing a “generic” mechanism to handle temporal data in Entity Framework
我正在嘗試使用Entity Framework完成一個“通用”機制來更新我的SQL Server數據庫中的時態數據。
我所做的是創建一個名為ITemporalData
的“標記”接口,它定義了需要存在的兩個屬性 - DateTime ValidFrom
和DateTime? ValidTo
DateTime? ValidTo
。
public interface ITemporalData
{
DateTime ValidFrom { get; set; }
DateTime? ValidTo { get; set; }
}
我希望在我的DbContext.SaveChanges()
覆蓋中實現“通用”方法:
ITemporalData
對象,它會給我一個新的對象來存儲( EntityState.Added
),並將其ValidFrom
值設置為當前日期和時間 .Reset()
),然后將該“舊”記錄的ValidTo
設置為當前日期和時間 雖然我可以輕松地過濾掉SaveChanges()
覆蓋中修改過的ITemporalData
對象,如下所示:
public partial class MyDbContext
{
// override the "SaveChanges" method
public override int SaveChanges()
{
DateTime currentDateTime = DateTime.Now;
// get the modified entities that implement the ITemporalData interface
IEnumerable<DbEntityEntry<ITemporalData>> temporalEntities = ChangeTracker.Entries<ITemporalData>().Where(e => e.State == EntityState.Modified);
foreach (var temporalEntity in temporalEntities)
{
// how would I do that, really? I only have an interface - can't clone an interface......
var cloned = temporalEntity.Entity.Clone();
// and once it's cloned, I would need to add the new record to the correct DbSet<T> to store it
// set the "old" records "ValidTo" property to the current date&time
temporalEntity.Entity.ValidTo = currentDateTime;
}
return base.SaveChanges();
}
}
我正在努力ITemporalData
“克隆修改后的記錄”方法 - 我實際上只有一個ITemporalData
接口 - 但克隆(使用AutoMapper或其他方法)總是取決於實際的底層具體數據類型.....
要克隆實體,您可以通過反射( Activator.CreateInstance
)創建新實例,並通過反射將所有原始(非導航)屬性復制到它。 最好不要使用自動映射工具,因為它們也會訪問導航屬性,這可能導致延遲加載(或者至少確保禁用延遲加載)。
如果您不喜歡反射(請注意,自動映射器仍將使用它) - 您還可以從ICloneable
繼承您的接口並為每個ITemporalData
實體實現Clone
方法(如果您的實體是自動生成的 - 使用部分類)。 然后每個實體決定如何克隆,沒有任何反射。 如果您的克隆邏輯很復雜(例如,涉及從導航屬性克隆相關對象),這種方式也會帶來好處。
要添加實體糾正DbSet,使用非類型化的設置方法DbContext
:
this.Set(temporalEntity.GetType()).Add(temporalEntity);
您可以將此Clone
方法添加到您的上下文中:
T Clone<T>(DbEntityEntry<T> entry)
where T : class
{
var proxyCreationEnabled = this.Configuration.ProxyCreationEnabled;
try
{
this.Configuration.ProxyCreationEnabled = false;
var clone = (T)entry.CurrentValues.ToObject();
Set(clone.GetType()).Add(clone);
return clone;
}
finally
{
this.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
}
}
並使用如下:
var cloned = Clone(temporalEntity);
clone.GetType
將返回克隆對象的實際類型,而T
將是編譯時類型ITemporalData
。
這使用EF自己的基礎設施來創建克隆,這無疑比反射更快。
雖然克隆的狀態立即設置為“已Added
,但它不會執行延遲加載。 但是,確保克隆永遠不會成為代理可能更安全,因此,如果您決定使用克隆執行其他操作,則永遠不會觸發延遲加載。 (感謝Evk的敏銳評論)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.