13

如果我有一组数据库表(例如,在 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捕获所有异常并将它们存储在 a 中List<ValidationException>,然后在最后将它们全部打印出来会更好?

我确实找到了这个,看起来它可能有效,但我希望能找到一些代码示例来解决。有什么建议么?过去有没有人做过类似的事情?

对于某些背景,该程序将使用 C# 或 VB.NET 编写,并且表很可能存储在 Access 或 SQL Server CE 中。

4

5 回答 5

18

只是对此的更新:我决定使用装饰器模式。也就是说,我有一个实现IValidateableTable接口(包含validate()方法)的“通用”表类。然后,我创建了几个验证装饰器(也就是implement IValidateableTable),我可以将它们包裹在我要验证的每个表周围。

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

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

然后,我需要做的就是调用table1.Validate()它通过调用所有需要的验证的装饰器展开。到目前为止,它似乎工作得很好,尽管我仍然愿意接受建议。

于 2008-09-16T13:50:23.427 回答
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");

达到这种效果的东西。

于 2008-09-05T15:28:58.353 回答
4

两种方法:

  1. CSLA,其中业务对象上的匿名方法用于验证。
  2. 阅读JP Boodhoo 的博客,其中他实现了一个规则引擎,并发布了非常详细的帖子和示例代码。您还可以在非常值得观看的DNR 电视节目中看到他的作品。
于 2008-09-21T16:47:19.067 回答
3

我会尝试结合使用工厂模式和访客模式:

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);
        }
    }
}
于 2017-06-16T04:48:32.530 回答
1

我认为您实际上是在谈论数据库世界中称为约束的概念。约束是数据库如何保证其包含的数据的完整性。将这种逻辑放在数据库中而不是应用程序中更有意义(甚至 Access 也提供了基本形式的约束,例如要求列中值的唯一性或列表中的值等)。
输入验证(单个字段)当然是另一回事,任何应用程序仍应执行该验证(以便在出现问题时向用户提供良好的反馈),即使数据库具有明确定义的表列约束。

于 2008-10-10T09:30:24.803 回答