Validation Context

Apr 19, 2011 at 10:06 PM
Edited Apr 19, 2011 at 10:12 PM

 

After reading through the documentation it looks like you can run multiple spec's against a single entity object but using the example code it appears to only run the one marked as default.

My problem domain is basically a super large consolidation of 11 similar systems and while almost all the validations are now the same across the board it will be impossible for all of them.  Being able execute multiple specs on one entity and phase them out (remove from the assembly) as best we can as consolidation happens is what i'm hoping to achieve.

I am looking at the lastest source posted on April 7th i believe.

Am I missing something?


//just trying to get something to work...
 public class ClientValidatorSpec1 : Validates<Client>
    { 
            public ClientValidatorSpec1()
            {
                //IsDefaultForType();
                Check(c => c.LastName).Required().MaxLength(1); //.And.IsAlpha();
            }
    }
    
    public class ClientValidatorSpec2 : Validates<Client>
    {
 
        public ClientValidatorSpec2()
        {   
            //IsDefaultForType();
            Check(c => c.LastName).Required().IsAlpha();
        }
    }

    public class MyValidationContext : SpecExpress.ValidationContext
    {
        public MyValidationContext()
        {
 
            AddSpecification<ClientValidatorSpec1>();
            AddSpecification<ClientValidatorSpec2>();
 
        }
    }

   var validationResults = ValidationCatalog<MyValidationContext>.Validate(client);
 
Thanks!         

 

Coordinator
Apr 20, 2011 at 3:31 AM
Edited Apr 20, 2011 at 3:33 AM

Well, you have an interesting problem, and one that ValidationContext wasn't really designed for.

Before I give you some options, let me clarify the issue. The ValidationCatalog is designed to resolve the Specification for an Entity without having to have actual knowledge of what or where that specification is. While you can register multiple specifications for a type, if you don't explicitly tell the catalog which one to use, it will try and figure it out by looking for the one with IsDefaultForType(). If the catalog ran all specifications, you might be running a specification for the DeleteClient context instead of SaveClient context. Also, it would be difficult to track down which specification caused what validation exception.

 

But all that won't help you solve your problem. There really isn't a good way to handle this with Contexts.  But, here are some possible approaches you could take:

1. Duplicate your rules. Sounds ugly at first, but not too bad when you look at it:

 

 public class ClientSpecificationSystem1 : Validates<Client>
    {
        public ClientSpecificationSystem1()
        {
            Check(c => c.LastName).Required().IsAlpha();
        }
    }
    public class ClientSpecificationSystem2 : Validates<Client>
    {
        public ClientSpecificationSystem2()
        {
            Check(c => c.LastName).Required().IsAlpha().And.MaxLength(25);
        }
    }

//Validate using a specific Specification
//Make a decision which Spec to use
var vn = ValidationCatalog.Validate<ClientSpecificationSystem2>(client);

 

2. Specification Inheritance. I'm not sure if your validation rules lend themselves to grouping like that, but you could build your specification up. You'll still end up with multiple specifications for a type, and you would have to explicitly define but you wouldn't repeat your rules. I imagine the tough part of this is organizing your rules:

 

 public class ClientSpecificationBase : Validates<Client>
    {
        public ClientSpecificationBase()
        {
            Check(c => c.LastName).Required().IsAlpha();
        }
    }

    public class ClientSpecificationExtended : Validates<Client>
    {
        public ClientSpecificationExtended()
        {
            IsDefaultForType();
            Using<Client, ClientSpecificationBase>();
            Check(c => c.LastName).Required().MaxLength(25);
        }
    }

   var validationNotificiation = ValidationCatalog.Validate<ClientSpecificationExtended>(client);

 

3. Use a Specification "Controller".You can apply specifications under different conditions, or just use this one specification to call out to other specification. I think this approach, maybe in conjunction with Inheritance is the best way, with what little I know about your Domain.

 

public class ClientSpecificationController : Validates<Client>
    {
        public ClientSpecificationController()
        {
IsDefaultForType();
//Make some decision about which Specification to use //by using an Lamba Expression and evaluating the object as a whole Check(c => c).If(c => c.ClientType == "Silver").Required().Specification<OldClientSpecification>(); Check(c => c).If(c => c.ClientType == "Gold").Required() .Specification<OldSpecificationSilver>().And .Specification<NewSpecificationGold>(); } }
//Can just call validate, because Catalog will default to ClientSpecificaitonController
  var validationNotificiation = ValidationCatalog.Validate(client);