跟踪复杂对象图中的更改

时间:2010-08-09 21:32:25

标签: .net architecture orm dto

我开始考虑在断开连接的应用程序中跟踪复杂对象图中的更改。我已经找到了几个解决方案,但我想知道您是否有最佳实践或使用什么解决方案,为什么?我把同样的问题传递给了MSDN forum,但我只得到了一个答案。我想从其他开发人员的经验中学到更多答案。

这个问题与.NET有关,所以对于实现细节的答案,我更喜欢与.NET世界相关的答案,但我认为在其他平台上也是如此。

我的案例中的理论问题在多层架构中定义(目前不一定是n层),如下所示:

  • 使用ORM处理持久性的存储库层(ORM工具目前无关紧要,但很可能是Entity Framework 4.0或NHibernate)。
  • 表示域对象的纯类(持久无知= POCO,相当于Java世界中的POJO)的集合。存储库保留这些类并将它们作为查询结果返回。
  • 使用域实体的域服务集。
  • 定义业务逻辑网关的Facade层。在内部,它使用存储库,域服务和域对象。域对象未公开 - 每个Facade方法都使用一组专用的数据传输对象作为参数和返回值。每个Facade方法都有责任将域实体转换为DTO,反之亦然。
  • 使用外观层和DTO的现代Web应用程序 - 我称之为断开连接的应用程序。通常,设计将来可能会发生变化,因此Facade层将被Web服务层包装,Web应用程序将使用该服务=>过渡到3层(Web,业务逻辑,数据库)。

现在假设其中一个域对象是具有订单详细信息(行)和相关订单的订单。当客户请求订购时,它可以修改订单,添加,删除或修改任何订单明细,以及添加或删除相关订单。所有这些修改都是在Web浏览器中的数据上完成的 - javascript和AJAX。因此,当客户端按下保存按钮时,所有更改都会在单个镜头中提交。问题是如何处理这些变化?存储库和ORM工具需要知道修改,插入或删除了哪些实体和关系。我以两个“最佳”解决方案结束了:

  1. 将DTO的初始状态存储在隐藏字段中(更糟糕的是会话)。当接收保存更改的请求时,基于接收的数据创建新的DTO,并基于持久的数据创建第二个DTO。合并这两个并跟踪更改。将合并的DTO发送到外观层,并使用收到的有关更改的信息来正确设置实体图。这需要在域对象中进行一些手动更改跟踪,以便可以从头开始设置更改信息,然后将其传递到存储库 - 这是我不太满意的一点。

  2. 根本不跟踪DTO中的更改。当在Facade层中接收修改后的数据时,创建修改后的实体并从存储库加载实际状态(通常是对数据库的附加查询 - 这是我不太满意的一点) - 合并这两个实体并自动跟踪ORM工具提供的实体代理的更改(实体框架4.0和NHibernate允许这样)。并发处理需要特别小心,因为实际状态不必是初始状态。

  3. 你怎么看?您有什么推荐的吗?

    我知道可以通过在某些应用程序层上使用缓存来避免其中一些挑战,但这是我目前不想使用的。

    我对这个话题的兴趣甚至更进一步。例如,假设应用程序进入3层体系结构,而客户端(Web应用程序)将不会用.NET编写= DTO类无法重用。跟踪DTO的变化将会更加困难,因为它需要其他开发团队在其开发工具中正确实现跟踪机制。

    我相信这些问题必须在很多应用中解决,请分享你的经验。

2 个答案:

答案 0 :(得分:3)

这完全是责任。

(我不确定这是不是你想要的那种答案 - 让我知道是不是我可以更新它。)

所以我们在一个系统中有多个层 - 每个层负责一个不同的任务:数据访问,UI,业务逻辑等。当我们以这种方式构建系统时,我们(除其他事项外)试图做出未来的改变通过使每个组件负责一项任务很容易 - 因此它可以专注于那一项任务并且做得很好。随着时间的推移,它还可以更容易地修改系统,并且需要进行更改。

在考虑DTO时,需要考虑类似的想法 - “如何跟踪变化?”例如。以下是我如何处理它:BL负责管理规则和逻辑;鉴于网络的无状态性质(这是我完成大部分工作的地方)我只是没有跟踪对象的状态并且明确地查找更改。如果用户正在传回数据(要保存/更新),我会将整个数据传回去而不关心已经更改的内容。

一方面,这似乎效率低下,但由于数据量不大,所以这不是问题;另一方面,由于过程更简单,所以“移动部件”越少越不会出错。

我如何传回数据? -

  • 我使用DTO(或者POCO会更准确);当我在BL和DAL之间交换数据时(通过接口/ DI),数据作为DTO(或它们的集合)进行交换。具体来说,我正在为单个实例使用结构,并为多个结构使用这些结构的集合。

  • DTO是在一个具有很少依赖关系的公共类中定义的。

  • 我故意设法限制DTO的数量是特定对象的创建(如“订单”) - 但同时如果有充分的理由,我会制作新的。通常我会有一个“胖”DTO,其中包含该对象可用的大部分/全部数据,我也可能有一个更精简的设计用于集合(用于列表等)。在这两种情况下,这些DTO都是pureyl,用于返回“阅读”信息。你必须记住责任 - 当BL要求数据时,它通常不会同时写回数据;所以DTO是“只读”这一事实更多的是符合干净的界面和架构,而不是商业规则。

  • 我总是为插入和更新定义单独的DTO - 即使它们共享完全相同的字段。这样可能发生的最糟糕的事情就是重复一些繁琐的代码 - 而不是依赖于依赖性和多个重用案例来解开。

最后 - 不要混淆DAL如何与UI的工作方式相混淆;让ORM做他们的事情,因为他们以给定的方式存储数据并不意味着它是唯一的方式。

最重要的是在图层之间指定有意义的界面。

管理改变的是BL的工作;让UI以最适合您的用户的方式工作,并让BL弄清楚它是如何处理的,而DAL(通过你的DI良好的干净界面)只是做了它所说的。

答案 1 :(得分:3)

我们的体系结构与您的体系结构非常相似,但在客户端和服务器上使用包含相同域对象(不完全相同 - 代码共享)的Silverlight客户端。我们的架构的关键点是简短的

  • 客户端具有域模型,并使用我自己实现的跟踪框架跟踪更改(它使用AOP,以便我可以在客户端使用POCO;我不知道框架,我想要域模型坚持不懈)
  • 整个模型存储在客户端上的一种远程存储库中。当我们保存这些更改时,将使用汇编程序提取更改树(通过我的更改跟踪框架)并在DTO中转换(确切地说是DataContracts但无关紧要)。 DTO具有跟踪状态标志(新的,已修改的,已删除的)。
  • 在服务器端(服务层由WCF webservices实现),DTO被转换为域对象并附加到ORM(在我们的例子中为NHibernate)。由于这个附加过程,我需要跟踪状态。可以通过ORM
  • 完成其他更改并保留

目前很难将复杂的图形附加到ORM,但我希望原因是我在使用NHibernate方面没有太多经验。

我们尚未完成,但似乎很有希望。

对于数据访问,我们尝试使用WCF Dataservices。但我不认为我们会使用它们,因为要求是使用DataContract。这导致从基于DataContract的LINQ查询转换为基于域对象的LINQ查询。如果域模型和数据交集非常不同(在某些迭代中会出现这种情况),那么这样做并不方便且难以实现。

有何考虑?