Data Validation Design Patterns

前端 未结 5 1524
灰色年华
灰色年华 2020-12-13 16:14

If I have a collection of database tables (in an Access file, for example) and need to validate each table in this collection against a rule set that has both common rules a

相关标签:
5条回答
  • 2020-12-13 16:22

    I'd return some type of ValidationSummary for each one... or an IList depending on how you want to structure it.

    you could also opt to do some magic like this:

    using(var validation = new ValidationScope())
    {
       ValidateTable1();
       ValidateTable2();
       ValidateTable3();
    
       if(validation.Haserrors)
       {
           MessageBox.Show(validation.ValidationSummary);
           return;
       }
    
       DoSomethingElse();
    }
    

    then the ValidateTable would just reach into the current scope, like this:

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

    something to that effect.

    0 讨论(0)
  • 2020-12-13 16:25

    Just an update on this: I decided to go with the Decorator pattern. That is, I have one 'generic' table class that implements an IValidateableTable interface (which contains validate() method). Then, I created several validation decorators (that also implement IValidateableTable) which I can wrap around each table that I'm trying to validate.

    So, the code ends up looking like this:

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

    Then, all I need to do is call table1.Validate() which unwinds through the decorators calling all of the needed validations. So far, it seems to work really well, though I am still open to suggestions.

    0 讨论(0)
  • 2020-12-13 16:29

    I would try with a combination of the Factory and Visitor patterns:

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-13 16:33

    I think you are really talking about a concept called constraints in the world of databases. Constraints are how a database guarantees the integrity of the data it contains. It makes much more sense to put this sort of logic in the database, rather than the application (even Access offers rudimentary forms of constraints, such as requiring uniqueness of values in a column, or values from a list, etc.).
    Input validation (of individual fields) is of course a different matter, and any application should still perform that (to provide nice feedback to the user in case of problems), even if the DB has well-defined constraints of the table columns.

    0 讨论(0)
  • 2020-12-13 16:42

    Two approaches:

    1. CSLA where anonymous methods on business objects are used for validation.
    2. Read JP Boodhoo's blog where he has implemented a rules engine and has very detailed posts and sample code published. You can also see him at work on DNR Tv episode that's well worth watching.
    0 讨论(0)
提交回复
热议问题