我的实体有这个:
public DateTime UpdatedAt { get; private set; }
我想在每次更新实体时都更新该属性,所以我有这个:
private void updateTimeStamp() => UpdatedAt = DateTime.UtcNow;
但是然后我需要记住在使实体发生变异的每种方法中都调用它,例如:
public void addOrder(Order order) {
//blah blah
updateTimeStamp(); // <----- musn't forget this!
}
我经常忘记调用该方法,因此我在考虑替代方法。我不知道哪个最好,或者是否有我没有考虑的东西。
选项1:初始化属性
public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
问题:即使我不想更新实体,也会每次设置一次。
选项2:属性
[UpdateTimestamp]
public void addOrder(Order order) {
//blah blah
}
问题:这并不能改善情况,基本上与原始问题相同。
选项3:在context.SaveChanges()
ChangeTracker.Entries()
.Where(e => e.Entity is MyEntity)
.Where(e => e.State == EntityState.Modified)
.Where(e => e.Entity as MyEntity)
.ToList()
.ForEach(e => { e.UpdatedAt = DateTime.UtcNow; });
问题:UpdatedAt
现在需要一个公共设置器。这违反了DDD原则。这是最简单的选择,但这意味着我必须放弃DDD的“始终有效”。
选项3:与3相同,但使用反射
与选项3相似,即使它具有私有设置程序,我也可以使用反射来更改属性。这很容易做到,而且反射和“慢”的事实并不重要,因为与数据库写操作相比,时间可以忽略不计。
问题:在基础架构而不是域中进行更改只是感觉不对。
否则,这是一个不错的选择。它不是纯DDD,但至少可以让我保护实体的不变式。
选项4:卸载到数据库提供程序或数据库本身
builder.Property(e => e.UpdatedAt).ValueGeneratedOnUpdate();
// or
builder.Property(e => e.UpdatedAt).HasComputedColumnSql("GETDATE()");
问题:不适用于所有提供程序(因为我使用的Npgsql不支持计算列,因此不适用于我)。这也将问题转移到基础架构中并移出了域。所以我觉得我再次打破了DDD。
那么...有什么我忘记的东西吗?如果您正在执行DDD,该如何处理?
答案 0 :(得分:1)
正如您已经指出的那样,选项1不适合您,因为您可以简单地获得订单而不更新任何内容,并且日期也将发生更改。
选项2可能还会违反DDD原则,因为您可以修改订单,并且在更改特定方法之前不会更改更新日期。
您剩下的只是选项3,考虑到DDD原理,它似乎不太麻烦,而且使用起来也最舒适。 你应该这样走。只需使用您的UpdatedAt属性和updateTimeStamp方法创建一个接口,然后在您的实体上实现即可。您可能应该创建一个基类并实现该方法。然后按照您的建议在SaveChanges方法中获取修改后的条目,并使用该方法而不是属性来避免将setter设置为私有。
有第4个选项,您可以使用反射和代理类,但仅满足您的要求就很困难了。