代码之家  ›  专栏  ›  技术社区  ›  jerhinesmith

数据验证设计模式

  •  12
  • jerhinesmith  · 技术社区  · 17 年前

    如果我有一个数据库表集合(例如,在一个访问文件中),并且需要根据一个规则集来验证集合中的每个表,该规则集既有所有表的公共规则,也有特定于一个表或表的子集的单个规则,那么有人能推荐一个好的设计模式来研究吗?

    具体来说,我希望避免类似以下代码:

    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中。

    5 回复  |  直到 7 年前
        1
  •  14
  •   biniam    7 年前

    只是更新一下:我决定 Decorator pattern . 也就是说,我有一个“generic”表类实现 IValidateableTable 接口(包含 validate() 方法)。然后,我创建了几个验证修饰符(也就是 implement IValidateableTable )我可以将其环绕在我要验证的每个表上。

    所以,代码最终看起来是这样的:

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

    那么,我只需要打电话 table1.Validate() 它通过调用所有所需验证的修饰符展开。到目前为止,它似乎工作得很好,尽管我仍然乐于接受建议。

        2
  •  5
  •   Ben Scheirman    17 年前

    我会为每一个返回一些类型的验证摘要…或者是一个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");
    

    有这样的效果。

        3
  •  4
  •   David Robbins    17 年前

    两种方法:

    1. CSLA 其中对业务对象使用匿名方法进行验证。
    2. JP Boodhoo's 他在博客中实现了一个规则引擎,并发布了非常详细的文章和示例代码。你也可以看到他在工作 DNR Tv 这一集值得一看。
        4
  •  1
  •   Galghamon    17 年前

    我觉得你在说一个叫做 constraints 在数据库的世界里。约束是数据库如何保证其所包含数据的完整性。把这种逻辑放在数据库中比放在应用程序中更有意义(甚至Access也提供了基本形式的约束,比如要求列中值的唯一性或列表中的值等)。
    输入验证(单个字段)当然是另一回事,任何应用程序都应该执行它(在出现问题时向用户提供良好的反馈),即使DB对表列有定义良好的约束。

        5
  •  1
  •   Andres A.    8 年前

    我将尝试结合工厂和访客模式:

    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);
            }
        }
    }