C# 检测聚合根中实体的更改

C# 检测聚合根中实体的更改,c#,architecture,persistence,ddd-repositories,C#,Architecture,Persistence,Ddd Repositories,我想看看人们可能会采取什么方法来检测作为其集合一部分的实体的变化。我有一些有用的东西,但我并不疯狂。基本上,我的存储库负责确定聚合根的状态是否已更改。假设我在聚合中有一个名为Book的聚合根和一个名为Page的实体。书籍包含一个或多个页面实体,存储在页面集合中 插入与更新场景主要是通过检查聚合根及其实体来确定是否存在密钥来完成的。如果键存在,则假定对象曾一度保存到基础数据源。这使其成为更新的候选对象;但仅就实体而言,这并不是决定性的。对于聚合根,答案是显而易见的,因为只有一个,并且它是奇点入口,

我想看看人们可能会采取什么方法来检测作为其集合一部分的实体的变化。我有一些有用的东西,但我并不疯狂。基本上,我的存储库负责确定聚合根的状态是否已更改。假设我在聚合中有一个名为
Book
的聚合根和一个名为
Page
的实体。
书籍
包含一个或多个
页面
实体,存储在
页面
集合中

插入与更新场景主要是通过检查聚合根及其实体来确定是否存在密钥来完成的。如果键存在,则假定对象曾一度保存到基础数据源。这使其成为更新的候选对象;但仅就实体而言,这并不是决定性的。对于聚合根,答案是显而易见的,因为只有一个,并且它是奇点入口,所以可以假设键的存在将指示操作。在我的例子中,这是一个可以接受的场景,再次保存聚合根本身,以便捕获修改日期

为了帮助促进实体本身的这种行为,我的
EntityBase
类包含两个简单属性:
IsUpdated()
IsDeleted()
。这两个选项都默认为false。我不需要知道它是否是新的,因为我可以根据前面提到的密钥的存在做出决定。实现上的方法(在本例中为页面)将具有将备份数据集
IsUpdated()
更改为true的每个方法

例如,Page有一个名为
UpdateSectionName()
的方法,它更改了
SectionName
属性的支持值,该属性是只读的。这种方法的使用是一致的,因为它允许在执行该数据设置的方法中使用验证器的逻辑连接点(防止实体进入无效状态)。最终的结果是,我必须输入一个
this.IsUpdated()=true

当聚合根目录被发送到
Save()
的存储库中时(逻辑切换到
Insert()
Update()
操作),它就可以在
书籍
中的
页面
集合上迭代,查找具有以下三种情况之一的任何页面:

  • 没有钥匙。将插入没有键的
    页面
  • IsDeleted=true删除胜过更新,删除将被提交-忽略
    页面的任何更新
  • IsUpdated=true将提交页面的更新
  • 这样做可以防止我盲目地更新Pages集合中的所有内容,例如,如果书中有几百个页面实体,这可能会让人望而生畏。我一直在考虑检索这本书的副本,并进行比较,只提交检测到的更改(基于存在和/或比较的插入、更新和删除),但这似乎是一种非常健谈的方式

    主要缺点是开发人员必须记住在实体中的每个方法中设置IsUpdated。忘记一个,它将无法检测该值的更改。我曾经玩弄过一种定制的备份存储的想法,它可以透明地为更改添加时间戳,从而使
    IsUpdated
    成为存储库可以用来聚合更新的只读属性

    存储库正在使用一个工作单元模式实现,它的操作基于向其添加聚合根时生成的时间戳。由于可能有多个实体排队等待操作,因此实体操作将在执行实体所属的聚合根操作后立即汇总并执行。我可以更进一步,创建另一个工作单元来处理实体操作,并基于实体中使用的某种事件跟踪(我假设市场上的一些ORM产品实现类似级别的功能)

    不过,在我继续朝这个方向前进之前,我很想听听关于这方面的想法/建议/经验

    编辑:了解一些可能有帮助的其他信息:

  • 我目前使用的语言是C#,尽管我试图尽可能多地保留特定于语言的信息,因为这更像是一个理论讨论
  • 存储库/服务/实体/等的代码基于Tim McCarthy在其著作《使用C#的.NET域驱动设计》中的概念以及上的支持代码。它提供了对所采取的方法类型的可运行的理解,尽管我所使用的方法基本上是从头重写的

  • 简言之,我的回答是我同意我的提议。它正在发挥作用,尽管我相信还有改进的余地。实际上,这些更改只花了很少的时间,所以我觉得在本例中,我并没有偏离KISS或YAGNI原则太远。:-)

    我仍然觉得在操作上存在与时间相关的问题,但我应该能够在存储库实现中解决这些问题。这不是理想的解决方案,但我不确定是否值得重新发明轮子来纠正一个可以在比修复时间更短的时间内避免的问题