域实体跟踪数据的最佳实践?基类还是作文?

时间:2013-02-22 14:25:38

标签: entity-framework inheritance domain-driven-design composition

大多数大型项目的一个常见方面是需要在许多域实体上使用通用跟踪数据。例如,大多数大型项目会跟踪许多域实体的以下属性:

DateTime DateCreated
User CreatedBy
DateTime LastModified
User LastModifiedBy

这些数据非常自我解释,数据用于跟踪谁对Domain对象做了什么。

问题是在为大型应用程序设计域模型时,处理此跟踪数据的最佳方法是什么。

经典的方法是使用基类,然后让相关的域类继承自该基类。但这会引发我的 favor composition over inheritance 警钟。我工作的项目越大,我就越拒绝继承,但这并不是说有些情况下它是最好的选择,例如在这种情况下。另一种继承解决方案是使用接口,但是虽然此解决方案耦合较少,但我没有看到很多在域实体上使用此方法的示例。

第二种方法是使用组合向每个域实体添加各种跟踪对象。唯一的问题是必须特别指示数据层不将它们表示为单独的表。这是一项次要任务,但如果没有回报则难以证明这一点。

处理跟踪数据的最后一种方法是配置数据层以透明地执行此操作。我认为可能使用 Entity-Framework 执行此操作,但过去没有实现此解决方案,这将是最前期的时间密集型解决方案。很难预见这种解决方案是否值得这么麻烦。

虽然这个问题看似客观,但这实际上是大多数大型项目必须以某种方式处理的常见任务。

设计用于跟踪元数据的域模型和/或大型项目的最佳方法是什么?

2 个答案:

答案 0 :(得分:2)

最佳做法通常是主观的,可能导致解决的问题。你什么时候应该继承?你什么时候撰写?学者花了数年时间争论抛出一个问题的细枝末节。具有简单界面的基本继承是务实和有效的。如果它是所有您的实体的标准功能,那么继承可能是更好的选择。

我有一个带有审计属性的基类,并为这些属性实现了一个接口。我使用以下代码拦截对context.SaveChanges()的调用。它很简单,也很有效。如果任何被跟踪实体未实现IAudit接口,则可以将其扩展为失败。

    public override int SaveChanges()
    {
        var entities = this.GetChangedAuditDataEntities();

        foreach (var entity in entities)
        {
            this.SetModificationInfo(entity);
        }

        return base.SaveChanges();
    }

    private IEnumerable<IAuditData> GetChangedAuditDataEntities()
    {
        return (
            from entry in _context.ChangeTracker.Entries()
            where entry.State != EntityState.Unchanged
            select entry.Entity)
            .OfType<IAuditData>();
    }

    private void SetModificationInfo(IAuditData entity)
    {
        entity.lastModifiedBy = _currentUser.Name;
        entity.lastModified = System.DateTime.Now;
    }

这是其中一个不会自动产生&#34;正确&#34;回答。如果Jon Skeet要回答它,那么它将被视为最佳实践。唯一正确的答案必须确认您的特定偏见,或者至少打出正确的知识分子。

多年前,继承很普遍,其中一个很大的推动因素是继承的价值构成&#34;口头禅。很好,但继承有它的地方。

我建议任何具有许多相同类型对象的架构层,例如域对象(单词&#34;域对象&#34;暗示公共层)可以通过具有共同基础来大大增强类。 System.Object就是一个很好的例子。我再给你一个例子。当我们为我们的解决方案定义异常装饰器时,我们决定扩展ToString()方法以创建一个唯一标识该对象的值。

public override string ToString()
{
    if (this is IAuditData)
    {
        IAuditDataidentifiable = this as IAuditData;
        return string.Format(@"{0} {{ id: {1}, ETag: {2} }}",
            identifiable.GetType().Name,
            identifiable.id,
            identifiable.ETag);
    }
    else return base.ToString();
}

7行代码 - 务实,简单,有效。从你的问题来看,你完全反对继承,这必须由许多烧焦的手指承担。我也是;-)但我仍然声称它是这个例子中更好的选择。

答案 1 :(得分:0)

我喜欢qujck在他的回答和评论中所说的内容,但我想补充一些内容,如果跟踪信息代表了一个基础设施概念,我将把它们放在一些负责审计或记录等的基础设施层中。 ,否则,如果它们是域概念,我真的想保留它们,因为它们属于域模型层,在这种情况下,我将使用复杂对象(值对象)来表示它们,并减轻我自己手动设置它们的负担每当我创建一个跟踪对象时,我会定义一个方法,只要我的工厂或IoC容器在请求跟踪对象时调用,我的观点就是集中更改并保留它们所属的概念。