문제

데이터베이스 테이블 컬렉션(예: 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");

그런 의미에서 뭔가.

두 가지 접근 방식:

  1. CSLA 비즈니스 개체에 대한 익명 메서드가 유효성 검사에 사용됩니다.
  2. 읽다 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);
        }
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top