簡體   English   中英

實現“通用”機制來處理實體框架中的時態數據

[英]Implementing a “generic” mechanism to handle temporal data in Entity Framework

我正在嘗試使用Entity Framework完成一個“通用”機制來更新我的SQL Server數據庫中的時態數據。

我所做的是創建一個名為ITemporalData的“標記”接口,它定義了需要存在的兩個屬性 - DateTime ValidFromDateTime? 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM