如果实体处于特定状态,如何强制执行任何字段(或特定字段)不得更改的约束?我正在尝试在我当前的项目(c#、mvc、nhibernate、castle)中使用DDD,并且我正在考虑检查实体处于特定状态时任何字段(或特定字段)的约束的最佳方法不改变,即。已预订发票(状态=已预订)不得更改金额字段。在服务层中,我得到了一些我需要映射到域对象(来自gui或web服务等)的DTO对象。映射完成后,我想验证我的对象——特别是我想检查我的问题中的特定约束。现在我在想:你怎么看这件事?你知道这有什么好的模式吗?还是我错过了什么,我需要改变我对这个约束的看法?在此先感谢您的帮助。DDD中的域对象是“自我验证的”。换句话说,客户端代码不可能破坏域规则,因为对象强制执行其内部不变量。例如:publicclassInvoice{privateMoney_amount;私人发票状态_state;publicvoidChangeAmount(MoneynewAmount){if(_state==State.Booked){thrownewInvalidOperationException("无法更改预订发票的金额。");}_amount=newAmount;}//外部代码(UI)可以使用这样的方法预先检查业务//规则,以避免InvalidOperationException。publicBooleanCanChangeAmount(){if(_state==State.Booked){返回假;}返回真;DDD示例的另一个示例:publicHandlingEvent(finalCargocargo,finalDatecompletionTime,finalDateregistrationTime,finalTypetype,finalLocationlocation,finalVoyagevoyage){...if(type.prohibitsVoyage()){thrownewIllegalArgumentException("事件类型不允许航行"+type);永远不要让您的UI框架将域对象视为哑数据容器。不幸的是,互联网上有很多示例,而且C#对getter和setter的强调令人鼓舞。如果您在不执行业务规则的情况下更改对象状态,您最终会得到“损坏的”对象。对于NHibernate尤其如此,因为它的Session会记住所有对象,并会在下一次提交或刷新时将它们很好地转储到数据库中。但这只是一个技术问题,主要的原因是您需要能够仅通过查看Invoice类来推理与Invoice相关的业务规则。另请注意,代码应基于UbiquitousLanguage。您应该看到诸如“发票”、“预订”、“金额”之类的词,而不是通用的“字段”、“属性”、“验证器”。更新:empi,感谢您重申您的问题。您可能想提出一个新问题。这是我要强调的引述正如我在其中一条评论中所说的那样——这个问题是我遇到的一个更大问题的一部分。我正在寻找一种标准方法来仅在域中定义域逻辑和约束,然后将其转换为gui和其他层。我没有看到任何共同需求的共同模式,其中域声明该字段无法编辑,并且此规则自动转换为禁用该字段并使其成为只读的gui逻辑。我正在寻找mvc堆栈中的示例解决方案。我觉得我正在重新发明轮子,大多数开发人员只是放弃并复制gui中的逻辑我认为您正在寻找一种方法来声明域中的所有内容,然后“生成”UI。喜欢MVC的裸体对象?我从未使用过这种方法,但我怀疑生成的UI是否会赢得美观或可用性竞赛。在我看来,UI中总会有一些业务逻辑的“重述”。一些域不变量过于复杂,涉及多个领域,需要存储库甚至外部服务。我不确定是否可以自动生成高质量的UI。我认为尝试这样做可能会改变您的模型以符合UI基础结构。在设计我的域对象时,我尽量不将它们视为数据集合,而是将其视为可以执行操作的对象。您不是提供对数据的直接访问(甚至通过getter和setter方法),而是提供与对象可能采取的操作相对应的方法。有时操作会更改多个数据字段。有时它可能只更改一个并且在功能上与setter没有什么不同,但它的命名是为了代表一个动作而不仅仅是数据修改。使用这种方法,您可以根据实体的状态轻松实现允许的操作。例如,对于发票,您可以添加或删除项目。这将更改总数,但不提供直接修改总数的权限。当您不再允许更改时,如果发票处于某种状态(例如已预订),通过从add或remove方法中抛出异常来强制执行该状态,以指示该方法在当前状态下无效。但是,其他方法可能仍然有效,例如与运输或发票付款相关的方法。除了这种方法之外,我还使用不同的实体来表示生命周期中不同点的相同数据。当发票处于活动状态时,它必须是可以对其执行操作的对象。但是,一旦达到最终状态,它仅用于查看和报告,不会更改任何数据。通过使用不同的实体,如ActiveInvoice和CompletedInvoice,它在应用程序中变得很清楚,它被用作流程的一部分并且仅用于查看。它还使处理可能来自不同表或只读视图的存档数据变得更加容易。如果对象只有两个状态,分别表示可变状态和不可变状态,而没有太多逻辑来允许对各种状态使用不同的方法,则可以使用EricLippert的“PopsicleImmutability”模式。它允许对对象进行更直接的修改,但一旦冻结就强制执行其不变性。虽然我找不到很好的参考资料(我可以发誓我几年前从MartinFowler那里听说过,但搜索他的网站就成功了),但我习惯于听到被称为“freezable”或“freezable”的概念.它通常与两股会计交易结合使用。具体来说,直到相应的项目被冻结时才会创建会计交易,此时不允许对可能更改余额的项目执行任何操作。在许多情况下,根本不会采取进一步的操作,除了可能会取消,冻结的项目实际上并没有改变,而只是导致添加附加事件。奇怪的是,Microsoft在与WPF完全不同的环境中实现了这一点。他们使用“可冻结”主要是指不再需要更改通知。实际上,如果您使用的是WPF,您可能会考虑查看Freezable类。否则,如果您想要一个真正通用的模式,我强烈建议您阅读Kozmic的动态代理教程。虽然这主要是炫耀CastleProxy功能的借口,但“可冻结”概念正是他选择实现的,他展示了一种使用通用可重用库实现此目的的方法,而无需在事后编写更多额外代码.虽然有很多代码可以完成这一切,但基本思想是编写一个拦截器,然后用它来创建一个代理:internalclassFreezableInterceptor:IInterceptor,IFreezable{publicvoidFreeze(){_isFrozen=true;}publicboolIsFrozen{get{return_isFrozen;}}publicvoidIntercept(IInvocationinvocation){if(_isFrozen&&invocation.Method.Name.StartsWith("set_",StringComparison.OrdinalIgnoreCase)){thrownewObjectFrozenException()。调用继续();}}publicstaticTFreezableMakeFreezable()whereTFreezable:class,new(){return_generator.CreateClassProxy(newFreezableInterceptor());请注意,上面的代码不是生产质量代码,它只是一个介绍。您应该阅读更多链接站点以获取更多信息。据我所知,类/接口代理确实是以独立于域的方式执行此操作的唯一方法。否则,您将不得不为每个可冻结类重新实现可冻结逻辑——也就是说,在属性设置器中放置大量if-then语句,并在设置状态时抛出FrozenException。以上是C#学习教程:如果实体处于某种状态,如何强制约束(比如任何字段(或特定字段))不得改变?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
