문제
데이터베이스 테이블 컬렉션(예: 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");
그런 의미에서 뭔가.
두 가지 접근 방식:
- CSLA 비즈니스 개체에 대한 익명 메서드가 유효성 검사에 사용됩니다.
- 읽다 JP Boodhoo의 그는 규칙 엔진을 구현했으며 매우 상세한 게시물과 샘플 코드를 게시한 블로그입니다.그가 일하는 모습도 볼 수 있다 DNR TV 볼만한 가치가 있는 에피소드.
내 생각에 당신은 정말로 다음과 같은 개념에 대해 이야기하고 있는 것 같습니다. 제약 데이터베이스의 세계에서.제약 조건은 데이터베이스가 포함된 데이터의 무결성을 보장하는 방법입니다.이러한 종류의 논리는 응용 프로그램보다는 데이터베이스에 두는 것이 훨씬 더 합리적입니다. 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);
}
}
}