例子:
SalesOrder 由 SalesOrderHeader 和一个或多个 SalesOrderItems 组成。在编辑现有的 SalesOrder 时,可以修改 SalesOrderHeader 并且可以添加、修改和删除 SalesOrderItems。所有更改必须保存在单个事务中。多个用户可以以乐观并发同时编辑 SalesOrder。
我相信在单个事务中完成保存的要求鼓励我们在单个服务调用中同时传达 SaleOrderHeader 和 SalesOrderItems。将子数据与其父数据打包的含义是,需要对子数据是否被添加、修改或删除有所了解。
子实体的更改跟踪可以发生在服务器或客户端上。
服务器上的更改跟踪
这个策略的想法是,客户端可以根据自己的意愿修改 SalesOrder,而无需跟踪添加、修改或删除了哪些 SalesOrderItem。SalesOrderItems 的状态将在调用保存服务时在服务器上确定。
服务器应该在服务调用之间保持无状态。这意味着服务器无法保留任何有关 SalesOrder 在检索和最终保存之间状态的信息。如果服务器通过将修改后的对象图与数据库对象图进行比较来确定其实体的状态,则剩下的唯一选择。
使用 nHibernate,有一个合并功能来完成此操作。使用实体框架,最高投票的功能请求是添加此功能。还有一个用于 EF 的开源实现,称为GraphDiff。
这在理论上听起来很棒,因为它使服务非常易于设计和使用。但是,我发现此策略存在两个主要问题。首先是性能。每次保存时必须将整个对象图发回。无论是否修改了 SalesOrderItem,都必须将其发回,否则服务器将假定它已被删除。第二个问题更为关键,它与并发有关。如果用户 1 将 SalesOrderItem 添加到 SalesOrder 并且用户 2 对相同的 SalesOrder 进行了更改,则当用户 2 保存时,服务器将假定用户 1 添加的 SalesOrderItem 应该被删除,因为它不包含在用户 2 的对象图中。我看不到在任何服务器端更改跟踪的实现中都可以防止这种情况发生。
客户端上的更改跟踪
另一种方法是让客户端跟踪其实体的更改并在调用保存服务时传达该状态。一个好处是客户端不需要发送其未更改的子实体。这有助于提高性能。缺点是所有实体都需要一个名为“ObjectState”的附加属性来跟踪它是否被添加、修改或删除。这使得服务器上的实体模型非常混乱,并且充满了与业务领域无关的关注点。这也让服务的不同消费者有责任维持这种状态。另一个问题是处理已删除的实体变得困难。SalesOrderHeader 是否应该维护已删除的 SalesOrderItems 列表?还是应该为 SalesOrderItems 分配一个必须由客户端 UI 过滤掉的已删除状态?
我知道微风javascript 库有自己的客户端实体跟踪实现,但我担心它的实现需要客户端和服务器端组件。服务层不应该隔离我们在任何一方使用的技术吗?如果非 JavaScript 客户想要使用我的服务怎么办?
问题
我认为这是一个常见的场景,应该由大多数服务实现来解决。我有没有做出任何不正确的假设,或者我做了什么不正常的事情或普通的事情?你实施了什么策略?有没有合理的替代方案?