Pregunta

Si tengo una colección de tablas de bases de datos (en un archivo de Access, por ejemplo) y necesito validar cada tabla de esta colección con un conjunto de reglas que tenga reglas comunes para todas las tablas, así como reglas individuales específicas para una o un subconjunto de tablas, ¿alguien puede recomendar un buen patrón de diseño para analizar?

Específicamente, me gustaría evitar códigos similares 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
}

Además, he decidido usar log4net para registrar todos los errores y advertencias, de modo que se pueda declarar cada método. void y no necesita devolver nada.¿Es esta una buena idea o sería mejor crear algún tipo de ValidationException que captura todas las excepciones y las almacena en un List<ValidationException> ¿Antes de imprimirlos todos al final?

encontré este, que parece que puede funcionar, pero espero encontrar algunos ejemplos de código con los que trabajar.¿Alguna sugerencia?¿Alguien ha hecho algo similar en el pasado?

Para algunos antecedentes, el programa se escribirá en C# o VB.NET y lo más probable es que las tablas se almacenen en Access o SQL Server CE.

¿Fue útil?

Solución

Solo es una actualización en esto:Decidí ir con el Patrón decorador.Es decir, tengo una clase de tabla 'genérica' que implementa una IValidateableTable interfaz (que contiene validate() método).Luego, creé varios decoradores de validación (que también implement IValidateableTable) que puedo incluir en cada tabla que intento validar.

Entonces, el código termina luciendo así:

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

Entonces, todo lo que tengo que hacer es llamar table1.Validate() que se desarrolla a través de los decoradores solicitando todas las validaciones necesarias.Hasta ahora parece funcionar muy bien, aunque todavía estoy abierto a sugerencias.

Otros consejos

Devolvería algún tipo de ValidationSummary para cada uno...o una IList dependiendo de cómo quieras estructurarla.

También puedes optar por hacer algo de magia como esta:

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

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

   DoSomethingElse();
}

entonces ValidateTable llegaría al alcance actual, así:

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

algo en ese sentido.

Dos enfoques:

  1. CSLA donde se utilizan métodos anónimos en objetos comerciales para la validación.
  2. Leer JP Boodhoo's blog donde ha implementado un motor de reglas y tiene publicaciones muy detalladas y código de muestra publicado.También puedes verlo trabajando en Televisión DNR episodio que vale la pena ver.

Creo que realmente estás hablando de un concepto llamado restricciones en el mundo de las bases de datos.Las restricciones son la forma en que una base de datos garantiza la integridad de los datos que contiene.Tiene mucho más sentido poner este tipo de lógica en la base de datos, en lugar de en la aplicación (incluso Access ofrece formas rudimentarias de restricciones, como exigir la unicidad de los valores en una columna, o valores de una lista, etc.).
La validación de entrada (de campos individuales) es, por supuesto, un asunto diferente, y cualquier aplicación aún debería realizarla (para proporcionar una buena respuesta al usuario en caso de problemas), incluso si la base de datos tiene restricciones bien definidas de las columnas de la tabla.

Probaría con una combinación de los patrones Factory y 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);
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top