Pergunta

Se eu tiver uma coleção de tabelas de banco de dados (em um arquivo do Access, por exemplo) e precisar validar cada tabela dessa coleção em relação a um conjunto de regras que tenha regras comuns em todas as tabelas, bem como regras individuais específicas para uma ou um subconjunto de tabelas, alguém pode recomendar um bom padrão de design para analisar?

Especificamente, gostaria de evitar código semelhante a:

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
}

Além disso, decidi usar o log4net para registrar todos os erros e avisos, para que cada método possa ser declarado void e não precisa devolver nada.Esta é uma boa ideia ou seria melhor criar algum tipo de ValidationException que captura todas as exceções e as armazena em um List<ValidationException> antes de imprimi-los todos no final?

eu encontrei esse, que parece funcionar, mas espero encontrar alguns exemplos de código para trabalhar.Alguma sugestão?Alguém já fez algo semelhante no passado?

Para alguns antecedentes, o programa será escrito em C# ou VB.NET e as tabelas provavelmente serão armazenadas no Access ou no SQL Server CE.

Foi útil?

Solução

Apenas uma atualização sobre isso:Eu decidi ir com o Padrão de decorador.Ou seja, tenho uma classe de tabela 'genérica' que implementa um IValidateableTable interface (que contém validate() método).Então, criei vários decoradores de validação (que também implement IValidateableTable) que posso envolver em cada tabela que estou tentando validar.

Então, o código acaba ficando assim:

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

Então, tudo que preciso fazer é ligar table1.Validate() que se desenrola através dos decoradores chamando todas as validações necessárias.Até agora, parece funcionar muito bem, embora ainda esteja aberto a sugestões.

Outras dicas

Eu retornaria algum tipo de ValidationSummary para cada um...ou um IList dependendo de como você deseja estruturá-lo.

você também pode optar por fazer alguma mágica como esta:

using(var validation = new ValidationScope())
{
   ValidateTable1();
   ValidateTable2();
   ValidateTable3();

   if(validation.Haserrors)
   {
       MessageBox.Show(validation.ValidationSummary);
       return;
   }

   DoSomethingElse();
}

então ValidateTable alcançaria apenas o escopo atual, assim:

ValidationScope.Current.AddError("col1", "Col1 should not be NULL");

algo nesse sentido.

Duas abordagens:

  1. CSLA onde métodos anônimos em objetos de negócios são usados ​​para validação.
  2. Ler JP Boodhoo's blog onde ele implementou um mecanismo de regras e publicou postagens muito detalhadas e exemplos de código.Você também pode vê-lo trabalhando em TV DNR episódio que vale a pena assistir.

Acho que você está realmente falando sobre um conceito chamado restrições no mundo dos bancos de dados.As restrições são como um banco de dados garante a integridade dos dados que contém.Faz muito mais sentido colocar esse tipo de lógica no banco de dados, em vez de no aplicativo (até mesmo o Access oferece formas rudimentares de restrições, como exigir exclusividade de valores em uma coluna, ou valores de uma lista, etc.).
A validação de entrada (de campos individuais) é obviamente uma questão diferente, e qualquer aplicativo ainda deve executá-la (para fornecer um bom feedback ao usuário em caso de problemas), mesmo que o banco de dados tenha restrições bem definidas das colunas da tabela.

Eu tentaria com uma combinação dos padrões Fábrica e Visitante:

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);
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top