【问题标题】:EF Code-First inherit a single base class to implement easy historocityEF Code-First 继承单个基类以实现简单的历史性
【发布时间】:2013-11-30 11:13:18
【问题描述】:

我在执行我的计划时遇到了一些错误,如下所述。在这一点上,我对解决特定错误并不那么感兴趣,因为我对这是否是一个好主意很感兴趣。

所有具有历史功能的对象都来自具有单个属性 public Guid ID { get; set; } 的公共类 AuditableObject

后代可能是:

public class Taco : AuditableObject { public string Seasoning { get; set; } }

现在,我想实现保存事件处理程序以写入下表(类)

public class AuditItem
{
    public Guid ID { get; set; }
    public virtual AuditableObject Object { get; set; }
    public string ObjectClassName { get; set; } //ugly
    public string OldObjectXMLData { get; set; }
    public string NewObjectXMLData { get; set; }
    public DateTime Timestamp { get; set; }
}

我不确定是否需要 ObjectClassName,因为我可以在运行时检查对象的类型,但它只是以防万一。

在保存时,我基本上会将对象前后序列化到各自的属性,保存时间戳,然后设置对象 - 即映射 FK。

这是一种丑陋的做法吗?像我这样使用 EF Code First 从单个类下降有什么明显的缺点吗?

【问题讨论】:

    标签: c# entity-framework inheritance database-design ef-code-first


    【解决方案1】:

    我认为在脱盐时您将需要对象类型的全限定名称,因此这是强制性的。

    另外序列化对象会导致问题。
    假设我们要使用approch对Taco类的TacoOject1进行审计,序列化后的数据会存入数据库,之后由于业务变化需要给Taco添加另一个属性,重新编译后需要对TacoOject1进行反序列化我们会得到TypeMissMatchException(不确定异常名称)。

    另一个设计反对意见是在审计过程中使用继承。
    首先:实际上 Taco is not a AuditableObject ,它是 Taco 玩的一个角色,使用继承将违反 Liskov Substitution Principle
    第二:不能使用多重继承,想想如果我们有一个TacoSupperClass,那我们怎么审计Taco呢?

    如果我要设计审计流程,我会使用Entity–attribute–value model
    AuditItem 设为标记接口并将其重命名为IAuditableEntity
    拥有一个名为 AuditableProperty 的属性会增强我们的流程。
    任何需要审计的实体都会被 IAuditableEntity 标记,任何需要参与审计的实体的属性都会被 AuditableProperty 属性标记。

    public class Taco : IAuditableEntity 
    { 
      [AuditableProperty]
      public string Seasoning { get; set; } 
    
      [AuditableProperty]
      public string OtherProperty1 { get; set; } 
    
      public string OtherProperty2 { get; set; } 
    
    }
    

    AuditLog 表将包含以下列:
    1.EntityFullTypeName:(字符串)我们将审计不同的实体,该字段将用于获取有意义的报告。(强制)
    2.ObjectIdentifier:被操作的实体标识符,实体的主键或业务键。
    3.FieldName:(字符串)实体字段名称。
    4.OldValue:(字符串)实体字段旧值。
    5.NewValue:(字符串)实体字段新值。
    6.TransactionUser:进行更改的应用程序用户。 (必填)
    7.TransactionID:任何更改实体的操作都需要有唯一的事务ID(如GUID)(强制),如果实体更新更改多个字段,这些列将是跟踪所有更改的关键点在更新中(交易)
    8.ChangeDate:交易日期。 (必填)
    9.FieldType:枚举或文本显示字段类型,如TEXT或Double。 (强制)

    在服务层中,当 Taco1 将被更新(或插入)时,我们将检查 Taco1 类型是否由 IAuditableEntity 使用反射标记(使用惰性 chash 存储反射数据),如果是,哪些属性已更改(我们需要一个单独的数据库调用来获取旧值)。
    例如:

    Taco1 = new Taco(); 
    Taco1.Seasoning = "old Seasoning value";
    Taco1.OtherProperty1 = "Old Other Property1 value";
    Taco1.OtherProperty2 = "Old Other Property2 value";
    

    之前保存,现在更新:

    Taco1.Seasoning = "New Seasoning value";
    Taco1.OtherProperty1 = "New Other Property1 value";
    Taco1.OtherProperty2 = "New Other Property2 value";
    

    我们将在 AuditLog 中插入两条具有相同 TransactionID 的记录:

    采用这种方法
    可以跟踪任何实体(表格)
    报告将是可读的
    只会记录更改。

    【讨论】:

    • 我喜欢。比仅仅序列化整个对象多一点工作,但显然是更好的设计。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多