我使用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();
}
答案 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持续更改实体之前,它会引发一个您可以收听并分配所需审计值的事件。
应该在域对象中设置用户还是在域对象中要求用户 业务层?
最好在基础架构/存储库级别使用上述审计实现来处理此问题。这是数据持续存在之前的最后一站,因此是处理此问题的理想场所。