9

我使用实体框架并想使用 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 模型看起来确实比上面的方法更混乱。

问题:

可以在域模型中使用 DateTime.Now 吗?您使用 DDD 和 EF Code First 将此类信息放在哪里?应该在域对象中设置用户还是在业务层中要求它?

更新

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

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

        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();
    }
4

2 回答 2

3

可以在域模型中使用 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”

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

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

    // for persistance
    protected YourModel()
    {}
}

然后有这些领域的私人二传手。

我收到很多 R# 警告“构造函数中的虚拟成员调用”,我以前读过它,这不应该是一个好习惯。

这通常不是问题。在这里阅读:构造函数中的虚拟成员调用

于 2012-10-18T08:55:46.183 回答
2

可以在域模型中使用 DateTime.Now 吗?

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

您使用 DDD 和 EF Code First 将此类信息放在哪里?

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

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

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

于 2012-10-17T19:24:15.277 回答