2012-02-16 33 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