假设您拥有规范的Customer域对象。您有三个不同的屏幕显示客户:外部管理员,内部管理员和更新帐户。
进一步假设每个屏幕仅显示Customer对象中包含的所有数据的子集。
问题是:当UI从每个屏幕(例如通过DTO)传回数据时,它仅包含完整Customer域对象的该子集。因此,当您将DTO发送到Customer Factory以重新创建Customer对象时,您只有部分客户。
然后您将此客户发送到您的客户存储库以保存它,并且一堆数据将被清除,因为它不在那里。随之而来的是悲剧。
所以问题是:你将如何处理这个问题?
我的一些想法:
包含一个参数 存储库指示哪个部分 客户更新,并忽略 其他
当您加载客户,将其保存在静态内存中,或在会话中或任何地方,然后当您从UI接收其中一个DTO时,仅更新与DTO相关的部分
IMO,这两个都是kludges。还有其他更好的想法吗?
@chadmyers:这是问题所在。
实体具有属性A,B,C和D.
DTO#1包含B和C的属性。
DTO#2包含C和D的属性。
UI要求DTO#1,您从存储库加载实体,将其转换为DTO#1,仅填充B和C,并将其提供给UI。
现在UI更新B并重新发送DTO。您重新创建了实体,并且只填充了B和C,因为这是DTO中包含的所有内容。
现在您要保存实体,该实体仅填充B和C,A和D为空/空。存储库无法知道它是否应该将持久性中的A和D更新为空白,或者是否应该忽略它们。
答案 0 :(得分:4)
我会在收到DTO后使用工厂从存储库加载完整的客户对象。之后,您只能更新在DTO中指定的那些字段。
例如,通过检查上次更新的时间戳,您还可以对客户应用一些乐观并发。
答案 1 :(得分:3)
这是一个网络应用程序吗?从repo加载客户对象,从DTO更新它,然后保存回来。这对我来说似乎不是什么好事。 :)
更新:根据您的更新(A,B,C,D示例)
所以我想的是当你加载实体时,它填充了A,B,C和D.如果DTO#1只更新B& C,没关系。 A和D不受影响(这是所希望的情况)。
存储库对B& S的影响。 C更新取决于他。例如,如果您正在使用Hibernate / NHibernate,它只会解决问题并发布更新。
仅仅因为DTO#1只有B& C并不意味着你也必须将A& D.别管它们。
答案 2 :(得分:1)
我首先忽略了这个问题的重点,因为它基于一些我认为从设计角度来看没有意义的事情。
从存储库中保存实体然后将其转换为DTO是一种浪费。我假设您的DAL将DTO传递到您的存储库,然后将其转换为完整的实体对象。因此将其转换回DTO似乎很浪费。
如果您的搜索结果页面显示大量记录并且仅显示部分实体数据,则有多个DTO是有意义的。在这种情况下,将该页面传递给它所需的数据是有效的。将包含部分数据的DTO传递到CRUD页面没有意义。只需给它一个完整的DTO甚至是一个完整的实体对象。如果它不使用所有数据,罚款,没有伤害。
所以主要的问题是我不认为你应该使用部分DTO将数据传递给这些页面。如果您使用了完整的DTO,则只要执行保存操作,我就会执行以下3个步骤:
此方法需要额外的数据库命中,但这在CRUD表单上确实不是一个重要问题。
答案 3 :(得分:1)
如果我们理解存储库处理(几乎完全)非常丰富的域实体,那么很多DTO都可以简单地映射回来。
即
dtoUser.MapFrom<In,Out>(Entity)
or
dtoAdmin.MapFrom<In,Out>(Entity)
您会反过来将dto信息返回给实体,依此类推。所以你的存储库只保存了丰富的实体,而不是很多DTO的
entity.Foo = dtoUser.Foo
or
entity.Bar = dtoAdmin.Bar
entityRepsotiry.Save(entity) <-- do not pass DTO.
DTO的重点在于为演示文稿保持简单,或者说对于WCF dataTransfer,它与存储库或实体无关。
此外,你永远不应该从DTO构建实体......获得实体的唯一两种方法分别是通过工厂(新)或存储库(现有)。
你提到将实体存储在某处,为什么要这样做?这是您的存储库的工作。它将决定在何处获取实体(db,cache,e.t.c),无需将其存储在其他地方。
希望有助于在您的域中分配责任,这总是一个挑战,并且这里和那里都有灰色区域,但一般来说,这些是Repository,DTO e.t.c的典型用途。