【发布时间】:2019-05-20 20:03:24
【问题描述】:
我的实体有这个:
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:卸载到 db 提供程序或 db 本身
builder.Property(e => e.UpdatedAt).ValueGeneratedOnUpdate();
// or
builder.Property(e => e.UpdatedAt).HasComputedColumnSql("GETDATE()");
问题:不适用于所有提供程序(不适用于我,因为我使用的是不支持计算列的 Npgsql)。这也将问题移入基础设施并移出域。所以我觉得我又要打破 DDD 了。
那么……有什么我忘记了吗?如果你在做 DDD,你如何处理这个?
【问题讨论】:
-
为什么需要公共设置器?
-
@Matthiee 我知道阴影属性...但我不确定它们在这种情况下会有什么帮助?
-
但是你可以使用
e.SetUpdatedAt()来代替,并保持属性设置器私有。 -
@DavidG 我不确定我是否遵循你的想法......但这不仅仅是关于属性设置器是否是公共的,或者我是否将它隐藏在公共方法后面 - 这里的 DDD 很重要是 1) 除了实体本身之外,任何代码都不应该更改它,以及 2) 只有当某些东西 *实际改变了。
标签: c# entity-framework-core domain-driven-design entity-framework-core-2.2