4

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

DateTime DateCreated
User CreatedBy
DateTime LastModified
User LastModifiedBy

该数据非常易于解释,该数据用于跟踪谁在何时对域对象做了什么。

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

经典的方法是使用基类,然后让相关的域类从该基类继承。但这引发了我对继承的青睐敲响了警钟。我从事的大型项目越多,我就越会拒绝继承,但这并不是说在某些情况下它是最佳选择,例如在这种情况下。另一种继承解决方案是使用interface,但是虽然这种解决方案的耦合度较低,但我没有看到很多在域实体上使用这种方法的示例。

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

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

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

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

4

2 回答 2

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;
    }

这是不会自动获得“正确”答案的问题之一。如果 Jon Skeet 回答这个问题,那么这将被认为是最佳实践。唯一的其他正确答案将必须确认您的特定偏见,或者至少要打出正确的知识点。

多年前,继承盛行,其中的众多阻力之一就是“价值构成优于继承”的口头禅。很好,但继承有它的位置。

我建议任何具有许多相同类型对象的架构层,例如域对象(“域对象”一词暗示公共层)可以通过拥有一个公共基类来大大增强。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行代码——务实、简单、有效。从你的问题来看,你听起来完全反对继承,继承必须来自许多烧伤的手指。我也是;-) 但我仍然断言在这种情况下它是更好的选择。

于 2013-02-22T14:51:48.717 回答
0

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

于 2013-02-24T00:31:45.930 回答