Domanda

Se ho una raccolta di tabelle di database (in un file Access, ad esempio) e devo convalidare ciascuna tabella in questa raccolta rispetto a un set di regole che ha sia regole comuni a tutte le tabelle sia regole individuali specifiche per uno o un sottoinsieme di tabelle, qualcuno può consigliare un buon modello di progettazione da esaminare?

Nello specifico, vorrei evitare codice simile 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
}

Inoltre, ho deciso di utilizzare log4net per registrare tutti gli errori e gli avvisi, in modo che ciascun metodo possa essere dichiarato void e non è necessario restituire nulla.È una buona idea o sarebbe meglio crearne una sorta ValidationException che cattura tutte le eccezioni e le memorizza in a List<ValidationException> prima di stamparli tutti alla fine?

L'ho trovato Questo, che sembra possa funzionare, ma spero di trovare effettivamente alcuni esempi di codice su cui lavorare.Eventuali suggerimenti?Qualcuno ha fatto qualcosa di simile in passato?

Per qualche informazione, il programma verrà scritto in C# o VB.NET e le tabelle saranno molto probabilmente archiviate in Access o SQL Server CE.

È stato utile?

Soluzione

Solo un aggiornamento su questo:Ho deciso di andare con il Modello decoratore.Cioè, ho una classe di tabella "generica" ​​che implementa un file IValidateableTable interfaccia (che contiene validate() metodo).Quindi, ho creato diversi decoratori di validazione (anche quello implement IValidateableTable) che posso racchiudere in ogni tabella che sto cercando di convalidare.

Quindi, il codice finisce per assomigliare a questo:

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

Allora non mi resta che chiamare table1.Validate() che si svolge attraverso i decoratori che chiamano tutte le convalide necessarie.Finora sembra funzionare davvero bene, anche se sono ancora aperto a suggerimenti.

Altri suggerimenti

Restituirei un tipo di ValidationSummary per ognuno...o un IList a seconda di come vuoi strutturarlo.

potresti anche scegliere di fare qualche magia come questa:

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

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

   DoSomethingElse();
}

quindi ValidateTable raggiungerebbe semplicemente l'ambito corrente, in questo modo:

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

qualcosa in tal senso.

Due approcci:

  1. CSLA dove per la convalida vengono utilizzati metodi anonimi sugli oggetti business.
  2. Leggere JP Boodhoo's blog in cui ha implementato un motore di regole e ha pubblicato post molto dettagliati e codice di esempio.Puoi anche vederlo al lavoro DNR TV episodio che vale la pena guardare.

Penso che tu stia davvero parlando di un concetto chiamato vincoli nel mondo dei database.I vincoli sono il modo in cui un database garantisce l'integrità dei dati in esso contenuti.Ha molto più senso inserire questo tipo di logica nel database, piuttosto che nell'applicazione (anche Access offre forme rudimentali di vincoli, come richiedere l'unicità dei valori in una colonna o dei valori da un elenco, ecc.).
La validazione dell'input (dei singoli campi) è ovviamente una questione diversa, e qualsiasi applicazione dovrebbe comunque eseguirla (per fornire un feedback positivo all'utente in caso di problemi), anche se il DB ha vincoli ben definiti delle colonne della tabella.

Vorrei provare con una combinazione dei modelli Factory e 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);
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top