C# 设置实体';使用EF Core和DDD时的s UpdatedAt属性

C# 设置实体';使用EF Core和DDD时的s UpdatedAt属性,c#,entity-framework-core,domain-driven-design,entity-framework-core-2.2,C#,Entity Framework Core,Domain Driven Design,Entity Framework Core 2.2,我的实体有: public DateTime UpdatedAt { get; private set; } 我希望在每次更新实体时都更新该属性,因此我有: private void updateTimeStamp() => UpdatedAt = DateTime.UtcNow; 但是,我需要记住在每一个改变实体的方法中调用它,例如: public void addOrder(Order order) { //blah blah updateTimeStamp();

我的实体有:

public DateTime UpdatedAt { get; private set; }
我希望在每次更新实体时都更新该属性,因此我有:

private void updateTimeStamp() => UpdatedAt = DateTime.UtcNow;
但是,我需要记住在每一个改变实体的方法中调用它,例如:

public void addOrder(Order order) {
  //blah blah
  updateTimeStamp();     // <----- musn't forget this!
}
问题:即使我不想更新实体,每次都会设置

选项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; });
builder.Property(e => e.UpdatedAt).ValueGeneratedOnUpdate();
// or
builder.Property(e => e.UpdatedAt).HasComputedColumnSql("GETDATE()");
问题:
UpdatedAt
现在需要一个公共setter。这违反了DDD原则。这是最简单的选择,但意味着我必须放弃DDD的“始终有效”

选项3:与3相同,但使用反射 与选项3类似,我可以使用反射来更改属性,即使它有一个私有setter。这很容易做到,而且它的反射和“慢”并不是什么大问题,因为与数据库写入相比,时间可以忽略不计

问题:在基础设施而不是域中进行更改感觉是错误的

否则这是个不错的选择。它不是纯DDD,但它至少允许我保护实体的不变量

选项4:卸载到数据库提供商或数据库本身

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; });
builder.Property(e => e.UpdatedAt).ValueGeneratedOnUpdate();
// or
builder.Property(e => e.UpdatedAt).HasComputedColumnSql("GETDATE()");
问题:不适用于所有提供程序(不适用于我,因为我使用的是不支持计算列的Npgsql)。同时,这也将问题转移到了基础设施和领域之外。所以我觉得我又犯了DDD


所以。。。有什么我忘了的吗?如果您正在进行DDD,您如何处理此问题?

正如您已经指出的,选项1不适用,因为您可以在不更新任何内容的情况下获得订单,并且日期无论如何都会更改

选项2可能也会违反DDD原则,因为您可能已经修改了订单,并且在使用该特定方法之前不会更改更新日期

你只剩下选项3了,考虑到DDD的原则,我想它不那么糟糕,而且使用起来也最舒适。 你应该走这边。只需使用UpdatedAt属性和updateTimeStamp方法创建一个接口,并在实体上实现它。您可能应该创建一个基类并实现该方法。然后按照建议在SaveChanges方法中获取修改后的条目,并使用该方法而不是属性来避免将setter设置为private


还有第四个选项,您可以使用反射和代理类,但这将是一个很长的路要走,只有你问…

为什么你需要一个公共setter?你试过@Matthiee我知道关于阴影属性。。。但是我不确定它们在这种情况下会有什么帮助?但是你可以做
e.SetUpdatedAt()
例如,让属性设置程序保持私有。@DavidG我不确定我是否同意你的想法。。。然而,这不仅仅是关于属性设置器是否是公共的,或者我是否将其隐藏在公共方法后面——这里的DDD问题是1)除了实体本身之外,任何代码都不可能更改它,2)只有在某些*实际更改时才可以。谢谢您的帮助。我从选项4开始。。。你认为现在最“好”的是什么?同意您关于要从上下文调用的
updateTimeStamp()
方法的想法,我最初就是这样做的,但它感觉非常错误(而且它与仅将属性设置为public没有太大区别)。我也考虑过反射的想法,但似乎是一种迂回的方式来完成它(我现在要把它添加到列表中)。我认为这是DDD变得不切实际的情况之一!:(如果你真的需要坚持DDD原则,我想你必须为每个属性创建一个私有属性,将公共属性绑定到私有属性,并在setter中调用update方法……你应该看看mvvm模式如何绑定属性,也许你可以在那里找到适合你的东西。最后,我认为你应该不要拘泥于一种单一的方法。有些设计对于某个特定的问题没有简单的解决方案,你需要做大量的工作来遵循它。有时为了简单易懂而做出让步是可以的。同意实用。