从聚合根中删除子
Posted
技术标签:
【中文标题】从聚合根中删除子【英文标题】:Delete a child from an aggregate root 【发布时间】:2012-12-13 14:00:45 【问题描述】:我有一个带有 Add、Update、Delete 的通用存储库。 我们将其命名为 CustomerRepository。
我有一个名为 Customer 的实体 (POCO),它是一个聚合根,带有 Addresses。
public class Customer
public Address Addresses get; set;
我处于分离实体框架 5 场景中。
现在,假设在获得客户后,我选择删除客户地址。 我通过 Update 方法将 Customer 聚合根提交到存储库。
如何保存对地址所做的修改?
-
如果地址id为0,我可以假设地址是新的。
对于地址的其余部分,我可以选择附加所有地址,并标记为已更新。
对于已删除的地址,我看不到任何解决方法...
我们可以说这个解决方案是不完整且低效的。
那么聚合根子节点应该如何更新呢?
我是否必须使用 AddAddress、UpdateAddress、DeleteAddress 等方法来完成 CustomerRepository?
这似乎会打破这种模式......
我是否在每个 POCO 上设置了 Persistence 状态:
public enum PersistanceState
Unchanged,
New,
Updated,
Deleted
然后在我的 CustomerRepository 中只有一种方法,Save?
在这种情况下,我似乎正在重新发明实体“非 POCO”对象,并将数据访问相关属性添加到业务对象......
【问题讨论】:
这些是 EF 实体吗?如果我错了,请纠正我,但是如果您加载聚合根(Customer
)并访问其导航属性(即Addresses
),这些不应该被延迟加载,附加到上下文并因此被保存/调用SaveChanges
时自动删除?
在我的例子中,客户是分离的,然后是附加的,而不是从 DbContext 加载的。 Poco 实体完全独立于 EF,不可能有延迟加载。
好的,所以您的意思是在您的存储库中,您加载客户(ENTITY POCO)并将其映射到 DOMAIN POCO,Customer
?然后在你想把它映射回一个 ENTITY POCO 并保存之前对它做一些操作?
不,POCO 是实体,这要归功于 POCO T4 模板。
天啊...您是否使用代理(从 DbContext
加载)?
【参考方案1】:
首先,您应该使用 Add、Update 和 Delete 方法来保留您的存储库,尽管我个人更喜欢 Add、indexer set 和 Remove,以便使存储库看起来像应用程序代码的内存集合。
其次,存储库应负责跟踪持久性状态。我什至不会用
弄乱我的域对象object ID get;
就像有些人一样。相反,我的存储库如下所示:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
AggregateRootDataModel 类用于跟踪我的内存中对象的 ID 以及跟踪任何持久性信息。在你的情况下,我会放置一个属性
List<AddressDataModel> Addresses get;
在我的 CustomerDataModel 类上,该类还将包含客户域对象以及客户的数据库 ID。然后,当客户更新时,我会有如下代码:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
public Customer this[int index]
set
//Lookup the data model
AggregateRootDataModel model = (from AggregateRootDataModel dm in this
where dm.Customer == value
select dm).SingleOrDefault();
//Inside the setter for this property, run your comparison
//and mark addresses as needing to be added, updated, or deleted.
model.Customer = value;
SaveModel(model); //Run your EF code to save the model back to the database.
这种方法的主要警告是您的域模型必须是引用类型,并且您不应该覆盖 GetHashCode()。这样做的主要原因是,当您执行匹配数据模型的查找时,哈希码不能依赖于任何可更改属性的值,因为即使应用程序代码修改了值,它也需要保持不变域模型实例的属性。使用这种方法,应用程序代码变为:
IAggregateRootRepository rep = new ConcreteRepository([arguments that load the repository from the db]);
Customer customer = rep[0]; //or however you choose to select your Customer.
customer.Addresses = newAddresses; //change the addresses
rep[0] = customer;
【讨论】:
在 n 层方案中,具体存储库是您的数据层,将位于您的 DAL 项目中。 Customer 对象是您的应用程序层的一部分,将在您的应用程序项目中。调用客户对象的代码也是应用层的一部分。话虽如此,您可以从使用 DDD 的洋葱架构中获得更多好处。您还可以在物理部署环境中获得更大的灵活性。洋葱架构的整个想法是您的业务逻辑(客户对象)不依赖任何东西。这样,客户对象就独立于部署场景。 Onion Architecture 这是一篇关于 Onion 架构的好文章。您还可以从这篇文章链接到最初创造该术语的 Jeffrey Palermo 的博客。 但是您正在使用对象引用来比较实体,或者在 n-itier 中,对象被序列化/反序列化,实体在旅行时会丢失其引用。您还必须处于有状态的场景中。 您不应该序列化/反序列化域实体。如果您的物理架构意味着表示层与应用程序层位于不同的机器上,那么您应该使用 DTO 和服务调用将您的表示关注点传达回应用程序层。在这种情况下,每个服务调用都处理该服务调用期间对象的状态。例如,如果您有一个服务方法 SaveCustomer(CustomerDTO),那么该服务方法将查看域实体是否存在,并更新现有实体或添加新实体。 明确地说,每个上下文都是一个有状态的场景。即使您在 Web 服务器上编写表示层,每个回发都是它自己的状态。在 Web 环境中,您无法避免必须加载存储库、向用户显示对象以及在用户进行更改时必须重新加载存储库。但是,您可以跟踪用户正在更改的内容,因此您只需在每个回帖上加载必要的对象。这样,在回发时,您只需将用户更改或要求对其执行某些操作的对象加载到存储库。【参考方案2】:简单的方法是使用自我跟踪实体What is the purpose of self tracking entities?(我不喜欢它,因为跟踪是不同的责任)。
困难的方法是,你拿原始集合然后比较:-/
Update relationships when saving changes of EF4 POCO objects
其他方式可能是事件跟踪?
【讨论】:
我不会使用 STE,因为它会打败我的 POCO 方法,而且微软现在不再推荐它 (msdn.microsoft.com/en-us/data/jj613668)。看来我必须加载我的聚合根并手动将其与分离的根进行比较... 我过去做过类似的事情:***.com/questions/6125871/…以上是关于从聚合根中删除子的主要内容,如果未能解决你的问题,请参考以下文章