2008-09-05 8 views
11

データベーステーブルのコレクション(Accessファイルなど)があり、このコレクションの各テーブルを、すべてのテーブルと個々のルールの両方に共通のルールを持つルールセットに対して検証する必要がある場合テーブルの1つまたはサブセットに固有のものですが、誰かが調べるには良いデザインパターンを推奨できますか?データ検証デザインパターン

具体的には、私はのようなコードを回避したいと思います。また

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 
} 

を、私は、各メソッドがvoidを宣言することができるように、エラーと警告のすべてを記録し、doesnのためにlog4netのを使うことにしました何かを返す必要はありません。これは良い考えですか、またはValidationExceptionを作成してすべての例外をキャッチし、最後にすべてを印刷する前にList<ValidationException>に保存する方が良いでしょうか?

私はthisを見つけました。うまくいくかもしれませんが、実際には動作するコードサンプルがほしいと思っています。助言がありますか?過去に誰かが似たようなことをしたことはありますか?

一部のバックグラウンドでは、プログラムはC#またはVB.NETで作成され、AccessまたはSQL Server CEに格納される可能性があります。

答えて

13

ちょうどこれに関するアップデート:私はDecorator patternと一緒に行くことに決めました。つまり、IValidateableTableインターフェイス(validate()メソッドを含む)を実装する1つの「汎用」テーブルクラスがあります。次に、検証するデコレータをいくつか作成しました(それも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

+0

不思議なことに、このデザインパターンにはよく知られた名前がありますか? – Phil

4

二つのアプローチ:ビジネスオブジェクト上の匿名メソッドは、検証に使用されている

  1. CSLA
  2. JP Boodhoo'sブログを読んでください。彼はルールエンジンを実装しており、非常に詳細な投稿とサンプルコードが公開されています。あなたはまた、彼が見ることができる価値があるDNR Tvエピソードの仕事で見ることができます。
1

実際には、データベースの世界でconstraintsと呼ばれるコンセプトについて話していると思います。制約とは、データベースに含まれるデータの整合性を保証する方法です。この種のロジックは、アプリケーションではなくデータベースに配置する方がはるかに理にかなっています(たとえアクセスが列の値の一意性やリストからの値などの一意性を要求するような基本的な制約を提供します)。
入力の検証(個々のフィールドの)は当然異なる問題であり、たとえ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); 
     } 
    } 
}