题
如果我有一组数据库表(例如,在 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>
最后将它们全部打印出来之前?
我确实找到了 这, ,看起来它可能有效,但我希望实际上找到一些可以使用的代码示例。有什么建议么?过去有人做过类似的事情吗?
对于某些背景,程序将用 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");
达到这种效果的东西。
我认为你真的在谈论一个叫做 限制条件 在数据库的世界里。约束是数据库如何保证其包含的数据的完整性。将这种逻辑放入数据库而不是应用程序中更有意义(甚至 Access 也提供了基本形式的约束,例如要求列中值的唯一性或列表中值的唯一性等)。
(各个字段的)输入验证当然是另一回事,任何应用程序仍然应该执行该操作(以便在出现问题时向用户提供良好的反馈),即使数据库对表列有明确定义的约束。
我会尝试结合工厂和访客模式:
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);
}
}
}