在DDD中放置创建日期和创建日期的位置?

时间:2012-10-17 18:32:55

标签: c# entity-framework design-patterns ef-code-first domain-driven-design

我使用Entity Framework并希望使用DDD原则。但是,有一些信息涉及记录/持久性信息和域对象信息之间的边界上的实体。

我的情况是这些被放在一个抽象的基类中,所有实体都继承自:

 public abstract class BaseEntity: IBaseEntity
{

    /// <summary>
    /// The unique identifier
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// The user that created this instance
    /// </summary>
    public User CreatedBy { get; set; }

    /// <summary>
    /// The date and time the object was created
    /// </summary>
    public DateTime CreatedDate { get; set; }

    /// <summary>
    /// Which user was the last one to change this object
    /// </summary>
    public User LastChangedBy { get; set; }

    /// <summary>
    /// When was the object last changed
    /// </summary>
    public DateTime LastChangedDate { get; set; }

    /// <summary>
    /// This is the status of the entity. See EntityStatus documentation for more information.
    /// </summary>
    public EntityStatus EntityStatus { get; set; }

    /// <summary>
    /// Sets the default value for a new object
    /// </summary>
    protected BaseEntity()
    {
        CreatedDate = DateTime.Now;
        EntityStatus = EntityStatus.Active;
        LastChangedDate = DateTime.Now;
    }

}

现在,如果不提供日期和时间,则无法实现域对象。 。不过我认为这是错误的地方。我可以说两者都是真的。也许根本不应该与域名混合?

由于我正在使用EF Code First,因此将它放在那里是有意义的,否则我需要创建从DAL中的基类继承的新类,复制代码并需要映射到两个域对象和MVC模型看起来比上面的方法更麻烦。

问题:

在Domain模型中使用DateTime.Now是否可以?你在哪里使用DDD和EF Code First来提供这类信息?是应该在域对象中设置用户还是在业务层中要求用户?

更新

我认为jgauffin在这里得到正确答案 - 但这确实是一个根本性的变化。但是,在我寻找替代解决方案时,我几乎已经解决了这个问题。如果添加或修改实体,我使用ChangeTracker.Entries查找ut并相应地设置字段。这是在我的UnitOfWork Save()方法中完成的。

问题是加载导航属性,例如User(正确设置DateTime)。可能是因为用户是实体继承的抽象基类的属性。我也不喜欢在那里放置字符串,但它可能会为某些人解决一些简单的场景,所以我在这里发布解决方案:

        public void SaveChanges(User changedBy)
    {
        foreach (var entry in _context.ChangeTracker.Entries<BaseEntity>())
        {
            if (entry.State == EntityState.Added)
            {
                entry.Entity.CreatedDate = DateTime.Now;
                entry.Entity.LastChangedDate = DateTime.Now;
                entry.Entity.CreatedBy = changedBy;
                entry.Entity.LastChangedBy = changedBy;
            }
            if (entry.State == EntityState.Modified)
            {
                entry.Entity.CreatedDate = entry.OriginalValues.GetValue<DateTime("CreatedDate");
                entry.Entity.CreatedBy = entry.OriginalValues.GetValue<User>("CreatedBy");
                entry.Entity.LastChangedDate = DateTime.Now;
                entry.Entity.LastChangedBy = changedBy;
            }
        }


        _context.SaveChanges();
    }

2 个答案:

答案 0 :(得分:3)

  

在Domain模型中使用DateTime.Now是否可以?

  

您使用DDD和EF Code First在哪里提供此类信息?是应该在域对象中设置用户还是在业务层中要求用户?

好。首先:DDD模型始终处于有效状态。这对公共制定者来说是不可能的。在DDD中,您使用方法处理模型,因为这些方法可以确保所有必需的信息都已指定且有效。

例如,如果您可以将项目标记为已完成,则可能也应更改UpdatedAt日期。如果你让调用代码确保它可能会被遗忘在某个地方。相反,你应该有类似的东西:

public class MyDomainModel
{
    public void MarkAsCompleted(User completedBy)
    {
        if (completedBy == null) throw new ArgumentNullException("completedBy");
        State = MyState.Completed;
        UpdatedAt = DateTime.Now;
        CompletedAt = DateTime.Now;
        CompletedBy = completedBy;
    }
}

阅读我关于该方法的博文:http://blog.gauffin.org/2012/06/protect-your-data/

<强>更新

  

如何确保没有人在以后更改“CreatedBy”和“CreatedDate”

我通常有两个适用于DB的构造函数。一个受保护的,可以由我的持久层使用,另一个需要必填字段。将createdby放在该构造函数中并在其中设置createdate:

public class YourModel
{
    public YourModel(User createdBy)
    {
        CreatedDate = DateTime.Now;
        CreatedBy = createdby;
    }

    // for persistance
    protected YourModel()
    {}
}

然后为这些字段设置私人设置器。

  

我得到了很多R#警告“构造函数中的虚拟成员调用”,我之前已经读过它并且它不应该是一个好习惯。

这通常不是问题。请在此处阅读:Virtual member call in a constructor

答案 1 :(得分:2)

  

在Domain模型中使用DateTime.Now是否可以?

这并不可怕,但问题是你最终不得不重复代码,而且更难以实现一致性。

  

您在哪里使用DDD和EF Code First来提供此类信息?

断言此类信息不属于您的域名,这是正确的。它通常称为审计日志或跟踪。有几种方法可以使用EF实现审计。例如,查看AuditDbContext - Entity Framework Auditing Context,或者只是搜索EF审计实现。我们的想法是,在EF持续更改实体之前,它会引发一个您可以收听并分配所需审计值的事件。

  

应该在域对象中设置用户还是在域对象中要求用户   业务层?

最好在基础架构/存储库级别使用上述审计实现来处理此问题。这是数据持续存在之前的最后一站,因此是处理此问题的理想场所。