DTO和实体是否都有输入验证

时间:2013-11-20 09:21:15

标签: c# wcf validation nhibernate domain-driven-design

我有一个WCF层,我的域模型在这个WCF层后面。我使用Nhibernate作为ORM工具,我的所有业务逻辑/数据访问等都将在这个WCF层之后。

我向我的客户揭露DTO。我有以下问题

1)我应该创建DTO吗?将实体直接暴露给wcf客户端有什么害处,因为我的实体也会有业务逻辑方法这样做我不得不用WCF属性来破坏我的权利对象,我觉得这样做不好?

2)如果我公开DTO,我应该验证DTO以及实体。如果我只验证DTO,那么我没有为我的Enitity对象提供任何输入验证。这样可以吗?

3)我应该考虑使用Schema验证来验证Application Service层(WCF层)中的DTO吗?或者我应该使用文章[博客]中给出的IValidator方法:http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/,如Jimmy Bogard所示

让DTO有时对我来说似乎是多余的,但我可以用它来为一个或多个实体提供详细信息。

我会将此服务暴露给各种客户端,因此我的DTO将从某些基础dto派生出具有凭据详细信息,我会在实际的wcf方法调用之前检查每个传入的请求(可能使用IEndpointBehaviour和IParamInspector)


修改

基于响应,我现在同意保留DTO层,这是一个例子,以便场景变得更加明确

假设我在我的WCF应用服务层中接受CustomerDetailsDTO的CreateCustomer方法可以由MVC应用程序调用。有一些输入验证,如

输入验证:

i)Name length should be greater than 2 but less than 50
ii) Age is mandatory and cann not be less than 18
(Different other field validations)etc 

商家验证:

There could then be some business rules to check for dupliate customer 
based on say email or some other factor whcih i think should be part of
my Domain business logic and should reside in CustomerEntity class.

当我们从客户端获取DTO时,输入验证应仅应用于服务接口层 或者它也应该应用于CustomerEntity

3 个答案:

答案 0 :(得分:6)

  

1)我应该创建DTO吗?将实体直接暴露给wcf客户端有什么害处,因为我的实体也会有业务逻辑方法这样做我不得不用WCF属性来破坏我的权利对象,我觉得这样做不好?

是的,SOA需要数据合同。

它们可以或多或少地形式化(CSV,JSON,XSD,WSDL,WADL甚至HTML或txt文件),但如果您无法就此类合同找到协议,则不应采用任何“服务”技术或技术(或任何其他IPC的重要性)。

Remoting是唯一试图避免此类要求的技术。这是一个惊人的想法,抽象,但具体来说它没有用。

  

2)如果我公开DTO,我应该验证DTO以及实体。如果我只验证DTO,那么我没有为我的Enitity对象提供任何输入验证。这样可以吗?

您应该验证“合同”,而不是业务规则。

例如,WCF DTO可能需要填充一些字段,我会在构造函数中使用ArgumentNullException

但你应该记住,DTO用于传输数据。如果您有一个数字字段,由于某些奇怪的原因必须将其作为字符串传输,您可以对其进行验证,例如阻止DTO的初始化。

  

3)我应该考虑使用Schema验证来验证Application Service层(WCF层)中的DTO吗?或者我应该使用文章[博客]中给出的IValidator方法:http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/,如Jimmy Bogard所示

如果您需要域名模型(这意味着您需要聘请专家来了解应用目的),必须是唯一负责业务规则的 。因此,对于简单的验证,您不需要任何验证框架。

您需要的是expressive exceptions,可以轻松映射到正确定义的故障。

编辑以回答新问题

在WCF中,我经常在DTO构造函数中使用输入验证,因此客户端无法发送“无效请求”。这具有许多优点,例如,客户端不能使用无效输入来配置DOS攻击。此外,如果您拥有大量客户端,这可以减少网络负载,并使用户体验更好一点,因为他不需要等待服务器响应就知道他忘记了电子邮件字段中的@。

但实际上,年龄超过18岁是商业规则,而不是输入规则。

输入规则可能是:“Age字段必须大于Zero”,因为负年龄不可能,零年龄声音太像用户错误(并且它是int32默认值)。

然而,合同验证不够

如果年龄与您的域名相关,则您将拥有一个Age结构,其中包含UInt32(因此之前的输入规则)。为什么要包裹UInt32?例如,因为在您的域模型中,您知道两个用户年龄的总和没有意义。

是的,你最多检查一次该号码(一个在客户端,两个在服务器上),但这是正确的,这里。 DTO可以独立于域模型发展,域模型不会冒意外行为(或者您根本不需要域模型)。

要了解业务规则,请考虑一个跟踪某种特殊治疗方法的医疗记录应用程序:command void Prescribe(Age patientAge, AntibioticPrescription prescription)可以检查PatientAge参数是否大于以前处方的年龄。这是商家规则。另一项商业规则应检查当前处方与前一处方之间的危险互动。

如果是这样,该命令应记录并抛出 3例外:

  • ArgumentNullException,当处方为空时(假设它是参考类型)
  • InconsistentAge,当提供的患者年龄低于最后一个时
  • MortalPrescription,当这样的处方可以杀死病人时。

此类异常表示preconditions业务规则(除了参数null,当程序员引入某种错误时,会尽快失败)。

答案 1 :(得分:3)

让DTO隐藏任何UI或外部处理中的域对象总是一件好事。这也允许您创建一个稍微不同的对象结构,因为您的域模型可能对数据库图表非常紧张,并且不能总是反映对象的逻辑含义。

意思是,不,你不应该直接通过服务公开实体。

将实体转换为DTO我在大多数情况下都使用Automapper,这非常方便。

关于验证,您可以在实体上使用数据注释来验证它们。例如。

但是,如果您的DTO反映了不可能单独使用注释验证的不同类型或更复杂的业务逻辑,那么您可以使用类似fluent validation的内容,它非常灵活且易于设置。

我目前正在一个项目中使用它们。 例如,对于标准验证,如必填字段,字段长度甚至电子邮件或电话号码验证,您可以使用带有正则表达式等的简单数据注释。

对于复杂的事情,如果字段A为空,字段B不能为空等...,您可以使用流畅的验证......这实际上取决于您是在实体还是DTO级别上执行此操作。如果您没有任何仅由DTO反映的自定义逻辑(可以是视图模型(就MVC而言),您可以在实体级别执行所有这些操作。

这还取决于您的应用程序是否是使用实体的唯一应用程序。如果您打算将您的实体和数据访问层公开为api给其他开发人员,那么大多数验证都应该在该级别上完成。

答案 2 :(得分:0)

  

我应该创建DTO吗?将实体直接暴露给wcf客户端是否有任何伤害

  1. 您应该创建DTO。当实体通过DataContract公开时,WCF会从客户端可能发送到服务器的任何垃圾中创建这些实体。即使您计划稍后验证状态,创建具有无效/不一致状态的实体也是不好的做法。
  2.   

    如果我公开DTO,我应该验证DTO以及实体。如果我只验证DTO,那么我没有为我的Enitity对象提供任何输入验证。这样可以吗?

    1. 无论您是否验证实体,都应验证DTO。如前所述,DTO包含来自客户端的任何垃圾。服务层负责验证每个服务方法参数值,并使用正确的值调用基础域层。

    2. 如果您确定在访问实体之前进行所有数据验证,则可以从实体中删除验证。