2012-02-16 81 views
2

我有一个关于图层之间数据验证的问题。作为一个例子,假设我有一个名为Book的对象,名为Title的字符串属性。同步层之间的验证值

在数据库中,我有一个标题的特定长度,它将决定我可以在标题属性中存储多少个字符。

我正在验证应用程序中每个图层之间的请求。因此,我验证了表示层中的用户输入,验证了对我的应用程序层的服务调用,并且在我尝试插入数据之前,SQL数据库显然会验证数据。

我的问题是,如果我对Title属性的长度有限,那么通过每个图层进行通信的最佳方式是什么?如果SQL Server表示长度不能超过40个字符,那么告诉其他图层不需要将长度值硬编码到每个图层中的最佳方法是什么。

你们在这种情况下做什么?

回答

2

第一关:

的SQL数据库将明显验证数据之前,我试图 插入

不,不会。如果您通过参数传递它,它将被截断。如果您正在运行直接的sql语句,那么在运行插入操作后,您将收到一个错误

这就是说,我们通过属性添加验证我们的对象,让企业库的验证之前,试图将数据传递到我们的数据库服务器揭开序幕。这使我们可以自定义每个属性和多种语言的消息。

实施例:

using Microsoft.Practices.EnterpriseLibrary.Validation; 
    using Microsoft.Practices.EnterpriseLibrary.Validation.Validators; 

    namespace MyApp.ObjectModel { 
     public class Account { 
     private String _accountNumber = String.Empty; 
     [StringLengthValidator(1, 50, MessageTemplateResourceName="ValidationStringLength", MessageTemplateResoourceType = typeof(MyApp.Properties.ErrorMessages), Tag="Account Number")] 
     public String AccountNumber { 
      get { return _accountNumber; } 
      set { _accountNumber = value; } 
     } 


     protected Validator BuildValidator() { 
      return ValidationFactory.CreateValidator<Account>(); 
     } // method::BuildValidator 


     public String Validate() { 
      Validator internalValidator = BuildValidator(); 
      ValidationResults info = internalValidator.Validate(this); 
      String result = String.Empty; 

      if (!info.IsValid) { 
      foreach(ValidationResult vr in info) { 
       result += vr.Message; 
      } 
      } 
      return result; 
     } // method::Validate 


     public Boolean Save() { 
      if (String.IsNullOrEmpty(Validate()) { 
      // perform the save operation. 
      } else { 
      // do something else, log the message or send it back to the screen or whatever. 
      } 
     } 
     } // class::Account 
    } 

上面类是使用企业库验证器的一个非常简单的例子。主要的一点是,AccountNumber属性中的属性基本上说帐号必须有1到50个字符。

我们将Validate()方法放在一个基类中,每当我们去持久化数据时就会调用它。同样,我们的验证方法实际上会返回一个错误集合,我们会过滤掉所有正在尝试保存对象的错误。接下来,我们使用控制反转模式将适当的数据层接口传递给对象本身。通过这种方式,我们可以将对象保存在自身内部,同时仍支持嘲笑功能以及随意更换持久性机制(即:数据库服务器)的能力。这不在上面的代码示例中。

本质上,这使我们能够保持业务类中的验证逻辑,而所有其他层可以是无知的,并简单地过滤任何错误的适当位置(通常是屏幕上的消息区)。如果您有专门的验证逻辑,添加自定义验证器并在需要的时候喷洒属性非常简单。

的最后一件事是,每个层可以在任何时候调用validate()方法,而不只是期间保存运算,以确保数据的一致性。

+0

好了,所以你怎么处理的值,如最大长度是多少?如果数据库中某个字段的最大长度为10,您是否会将值10硬编码到您的图层中?或者你使用中央配置? – Chris 2012-02-16 03:48:56

+0

@ChrisPaynter:10被烘焙到类定义中。所有图层都依赖于对象来确定它是否有效。 – NotMe 2012-02-16 03:52:51

+0

非常感谢。所以我猜你正在使用一个公共const int? – Chris 2012-02-16 04:27:38

0

我希望有一个机制,让我打(在UI和业务层)

为什么数据库。之前验证我的实体?

好的数据库通常不会容忍任何违反定义的模式的情况,它们通常会抛出非常糟糕的异常,特别是如果您使用像NHibernate或Entity Framework这样的持久框架(这些框架通常使用工作模式,并尝试在一次完成所有的东西,如果出现问题没有保证,你可以再次拍摄:))

如何?

在大多数的解决方案,这意味着元数据(XML配置文件或.net属性) ,这意味着制造与database.MVC框架同步这些元数据具有这样的机制开箱这在我看来是相当的cool 我从来没有看到这种元数据解决方案的替代方案,但也许我们可以使用数据库的实际模式即时构建元数据。

警告用户这些违规行为的政策可能会有所不同。 有些人更喜欢在出现问题时立即提醒用户(按字段进行验证),有些人更喜欢在提供所有数据(通过表单验证)后提醒用户,但有一点很明确:应该有一个单一的基础设施负责用于在两种情况下验证模型,并且此基础结构应独立于模型,因此可以重用。

2

我不认为有一个开箱即用解决方案,会做什么(即你的域验证领带到您的分贝)

但一些聪明的,我们可以实现的东西,将节省您大量的额外工作。

我建议考虑使用一个框架像FluentValidation

这将允许你创建你可以用它来在整个应用程序层验证您的域模型验证类。

因此,每个模型只需要一个验证类,然后数据库会让您知道该级别的任何问题。

或者,如果您需要,您可以创建一个验证类,每个图层,每个场景或真正你想要的。

看一看一些执行代码CodePlex从下面:

using FluentValidation; 

public class CustomerValidator: AbstractValidator<Customer> { 
    public CustomerValidator() { 
    RuleFor(customer => customer.Surname).NotEmpty(); 
    RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please specify a first name"); 
    RuleFor(customer => customer.Company).NotNull(); 
    RuleFor(customer => customer.Discount).NotEqual(0).When(customer => customer.HasDiscount); 
    RuleFor(customer => customer.Address).Length(20, 250); 
    RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode"); 
    } 

    private bool BeAValidPostcode(string postcode) { 
    // custom postcode validating logic goes here 
    } 
} 

Customer customer = new Customer(); 
CustomerValidator validator = new CustomerValidator(); 
ValidationResult results = validator.Validate(customer); 

bool validationSucceeded = results.IsValid; 
IList<ValidationFailure> 

failures = results.Errors; 
+0

+1:与我的概念相同,但是有一个替代EntLib的概念。 – NotMe 2012-02-16 15:22:13