2008-09-05 82 views
11

如果我有一組數據庫表(例如,在Access文件中),並需要根據規則集來驗證此集合中的每個表,該規則集在所有表中都具有通用規則以及單個規則特定於一個或一個表子集,有人可以推薦一個好的設計模式來看看?數據驗證設計模式

具體來說,我想避免類似代碼:

void Main() 
{ 
    ValidateTable1(); 
    ValidateTable2(); 
    ValidateTable3(); 
} 

private void ValidateTable1() 
{ 
    //Table1 validation code goes here 
} 

private void ValidateTable2() 
{ 
    //Table2 validation code goes here 
} 

private void ValidateTable3() 
{ 
    //Table3 validation code goes here 
} 

而且,我已經決定使用log4net的記錄所有的錯誤和警告,讓每個方法可以聲明void,並沒有按不需要返回任何東西。這是一個好主意,還是創建某種類型的ValidationException可以捕獲所有異常並將它們存儲在List<ValidationException>中,然後纔將它們全部打印出來?

我的確發現了this,看起來好像可以工作,但我希望能找到一些代碼示例來解決問題。有什麼建議麼?有沒有人在過去做過類似的事情?

對於一些背景,程序將使用C#或VB.NET編寫,並且這些表很可能會存儲在Access或SQL Server CE中。

回答

13

只是一個更新:我決定去與Decorator pattern。也就是說,我有一個'通用'表類實現了IValidateableTable接口(其中包含validate()方法)。然後,我創建了幾個驗證裝飾器(也是implement IValidateableTable),我可以環繞每個我試圖驗證的表。

所以,代碼最終看起來像這樣:

IValidateableTable table1 = new GenericTable(myDataSet); 
table1 = new NonNullNonEmptyColumnValidator(table1, "ColumnA"); 
table1 = new ColumnValueValidator(table1, "ColumnB", "ExpectedValue"); 

然後,所有我需要做的就是調用table1.Validate()它通過調用裝飾所有需要驗證的開卷。到目前爲止,它似乎工作得很好,儘管我仍然樂於接受建議。

5

我會返回某種類型的ValidationSummary每個人......或一個IList取決於你想如何構造它。

你也可以選擇做一些神奇的是這樣的:

using(var validation = new ValidationScope()) 
{ 
    ValidateTable1(); 
    ValidateTable2(); 
    ValidateTable3(); 

    if(validation.Haserrors) 
    { 
     MessageBox.Show(validation.ValidationSummary); 
     return; 
    } 

    DoSomethingElse(); 
} 

那麼ValidateTable只會達到到當前的範圍,就像這樣:

ValidationScope.Current.AddError("col1", "Col1 should not be NULL"); 

大意的東西。

+0

的設計模式是很不錯,但通常情況下,像這樣的一個簡單的解決方案是超過可以接受的解決問題。 – Samuel 2013-03-18 03:54:37

4

兩種方法:

  1. CSLA其中在業務對象上匿名方法被用於驗證。
  2. 閱讀JP Boodhoo's博客他已經實施了一個規則引擎,並且發佈了非常詳細的帖子和示例代碼。你也可以看到他在DNR Tv插曲中的作品,值得一看。
1

我想你真的在談論一個叫做constraints的概念,在數據庫的世界裏。約束條件是數據庫如何保證其包含的數據的完整性。將這種邏輯放在數據庫中,而不是應用程序(甚至Access提供基本的約束形式,例如需要列中值的唯一性,或列表中的值等),將其更合理。
輸入驗證(個別字段)當然是不同的事情,並且任何應用程序仍應該執行該操作(以便在出現問題時向用戶提供良好的反饋),即使DB具有明確定義的表列約束。

0

我會嘗試與工廠和訪客模式的組合:

using System; 
using System.Collections.Generic; 

namespace Example2 
{ 
    interface IVisitor 
    { 
     void Visit(Table1 table1); 
     void Visit(Table2 table2); 
    } 

    interface IVisitable 
    { 
     void Accept(IVisitor visitor); 
    } 

    interface ILog 
    { 
     void Verbose(string message); 
     void Debug(string messsage); 
     void Info(string message); 
     void Error(string message); 
     void Fatal(string message); 
    } 

    class Error 
    { 
     public string Message { get; set; } 
    } 

    class Table1 : IVisitable 
    { 
     public int Id { get; set; } 
     public string Data { get; set; } 
     private IList<Table2> InnerElements { get; } = new List<Table2>(); 

     public void Accept(IVisitor visitor) 
     { 
      visitor.Visit(this); 

      foreach(var innerElement in InnerElements) 
       visitor.Visit(innerElement); 
     } 
    } 

    class Table2 : IVisitable 
    { 
     public int Id { get; set; } 
     public int Data { get; set; } 

     public void Accept(IVisitor visitor) 
     { 
      visitor.Visit(this); 
     } 
    } 

    class Validator : IVisitor 
    { 
     private readonly ILog log; 
     private readonly IRuleSet<Table1> table1Rules; 
     private readonly IRuleSet<Table2> table2Rules; 

     public Validator(ILog log, IRuleSet<Table1> table1Rules, IRuleSet<Table2> table2Rules) 
     { 
      this.log = log; 
      this.table1Rules = table1Rules; 
      this.table2Rules = table2Rules; 
     } 

     public void Visit(Table1 table1) 
     { 
      IEnumerable<Error> errors = table1Rules.EnforceOn(table1); 

      foreach (var error in errors) 
       log.Error(error.Message); 
     } 

     public void Visit(Table2 table2) 
     { 
      IEnumerable<Error> errors = table2Rules.EnforceOn(table2); 

      foreach (var error in errors) 
       log.Error(error.Message); 
     } 
    } 

    class RuleSets 
    { 
     private readonly IRuleSetFactory factory; 

     public RuleSets(IRuleSetFactory factory) 
     { 
      this.factory = factory; 
     } 

     public IRuleSet<Table1> RulesForTable1 => 
      factory.For<Table1>() 
       .AddRule(o => string.IsNullOrEmpty(o.Data), "Data1 is null or empty") 
       .AddRule(o => o.Data.Length < 10, "Data1 is too short") 
       .AddRule(o => o.Data.Length > 26, "Data1 is too long"); 

     public IRuleSet<Table2> RulesForTable2 => 
      factory.For<Table2>() 
       .AddRule(o => o.Data < 0, "Data2 is negative") 
       .AddRule(o => o.Data > 10, "Data2 is too big"); 
    } 

    interface IRuleSetFactory 
    { 
     IRuleSet<T> For<T>(); 
    } 

    interface IRuleSet<T> 
    { 
     IEnumerable<Error> EnforceOn(T obj); 
     IRuleSet<T> AddRule(Func<T, bool> rule, string description); 
    } 

    class Program 
    { 
     void Run() 
     { 
      var log = new ConsoleLogger(); 
      var factory = new SimpleRules(); 
      var rules = new RuleSets(factory); 
      var validator = new Validator(log, rules.RulesForTable1, rules.RulesForTable2); 

      var toValidate = new List<IVisitable>(); 
      toValidate.Add(new Table1()); 
      toValidate.Add(new Table2()); 

      foreach (var validatable in toValidate) 
       validatable.Accept(validator); 
     } 
    } 
}