質問

データベース テーブルのコレクション (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
}

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

私は見つけました これ, 、これは機能するように見えますが、実際に機能するコードサンプルをいくつか見つけたいと思っています。助言がありますか?過去に同じようなことをした人はいますか?

背景として、プログラムは C# または VB.NET で作成され、テーブルは Access または SQL Server CE に保存される可能性が高くなります。

役に立ちましたか?

解決

これに関する最新情報:と一緒に行くことにしました デコレータパターン. 。つまり、 IValidateableTable インターフェイス (含まれる validate() 方法)。次に、いくつかの検証デコレーターを作成しました (これも implement IValidateableTable)これを検証しようとしている各テーブルをラップできます。

したがって、コードは次のようになります。

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

あとは電話するだけです table1.Validate() これは、必要な検証をすべて呼び出すデコレータを通じて展開されます。今のところ、非常にうまく機能しているようですが、まだ提案を受け付けています。

他のヒント

それぞれについて何らかのタイプの 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");

そういう趣旨の何か。

2 つのアプローチ:

  1. CSLA ビジネス オブジェクトの匿名メソッドが検証に使用されます。
  2. 読む JPブードゥーズ 彼はこのブログでルール エンジンを実装し、非常に詳細な投稿とサンプル コードを公開しています。彼が働いているところを見ることもできます DNRテレビ 見る価値のあるエピソード。

あなたは本当に という概念について話していると思います 制約 データベースの世界では。制約は、データベースがデータベースに含まれるデータの整合性を保証する方法です。この種のロジックをアプリケーションではなくデータベースに配置する方がはるかに合理的です (Access でも、列内の値の一意性やリストの値などを要求するなど、基本的な形式の制約が提供されています)。
もちろん、(個々のフィールドの) 入力検証は別の問題であり、DB にテーブル列の明確に定義された制約がある場合でも、アプリケーションは (問題が発生した場合にユーザーに適切なフィードバックを提供するために) 入力検証を実行する必要があります。

Factory パターンと Visitor パターンを組み合わせて試してみます。

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);
        }
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top