views:

112

answers:

2

On the assumption that I have Entity with couple of fields. Some fields are required at some specific state but others only on further/other state.

public class Entity
{
    //Required always
    public SomeReference {}

    //Required in specific situation/scenario
    public OtherReference {}
}

How to achieve that scenario with some known validation framework or how to do it by my self?

For help: Udi Dahan has some thoughts on this. http://www.udidahan.com/2007/04/30/generic-validation/

A: 

My preferernce is to localize common validation functions such as email and date validations into a ValidationService class that I can pass my object into. For the rest though I tend to put the validation into the class itself. If I am using LINQ to SQL then I can create a Validate() method on my object which LINQ to SQL will call prior to saving the object to the db like this:

public void Validate()
{
    if(!IsValid)
        throw new ValidationException("Rule violations prevent saving");
}

public bool IsValid
{
    get { return GetRuleViolations().Count() == 0;}
}

public IEnumerable<RuleViolation> GetRuleViolations()
{
    if(this.TermID == 0)
        yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(1), "agreeWithTerms");

    if(ValidationService.ValidateDate(this.Birthdate.ToString()))
        yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(2), "birthDate");

    if (!(Username.Length >= ConfigurationService.GetMinimumUsernameLength()) ||
        !(Username.Length <= ConfigurationService.GetMaximumUsernameLength()))
        yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(5), "username");

    if(ValidationService.ValidateUsernameComplexity(Username))
        yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(6), "username");

    if (AccountID == 0 && ObjectFactory.GetInstance<IAccountRepository>().UsernameExists(this.Username))
        yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(7), "username");

    if (!ValidationService.ValidateEmail(Email))
        yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(8), "email");

    if (AccountID == 0 && ObjectFactory.GetInstance<IAccountRepository>().EmailExists(this.Email))
        yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(9), "email");

    yield break;
}

Read here for a full understanding of this: http://nerddinnerbook.s3.amazonaws.com/Part3.htm

Andrew Siemer
+1  A: 

Hi, I have a solution that I am using at the moment. I use Fluent validation and am still getting used to it. I can give you an example of a simple scenario I have. maybe it helps. I have a user class, with a address Object property. At some point, I want to only validate the User details(name, email, password, etc) and at another state I want to validate the user address(first line, postcode, etc).

Classes look like this:

public class User {
 public virtual string Name { get; set; }
 public virtual string Email { get; set; }
 public virtual string Password { get; set; }
 public virtual Address Address { get; set; }  
}

public class Address {
 public virtual string Address1 { get; set; }
 public virtual string PostCode { get; set; }
}

I then have two (simplfied) validators, one for an address and one for a user:

    public AddressValidator() {
  RuleFor(address => address.Address1)
   .NotNull()
   .WithMessage("Please enter the first line of your address");

  RuleFor(address => address.PostCode)
   .NotNull()
   .WithMessage("Please enter your postcode")
   .Matches(UK_POSTCODE_REGEX)
   .WithMessage("Please enter a valid post code!");
 }

 public UserValidator() {
  RuleFor(user => user.FirstName)
   .NotNull()
   .WithMessage("Please provide a first name")
   .Length(3, 50)
   .WithMessage("First name too short");

  RuleFor(user=> user.Password)
   .Length(8, 50)
   .WithMessage("Password is too short");
 }

I then create a Model Validator, so for example, say we have a form where the user enters an address, we create a AddressModelValidator, and can re-use the validators we have written:

    public AddressModelValidator() {
  RuleFor(user => user.id)
   .NotNull()
   .WithMessage("An error has occured, please go back and try again");

  RuleFor(user => user.Address).SetValidator(new AddressValidator());
 }

So, with some thought, you can really create some nice models, and reduce your validation code duplication!

Dai Bok
This is close but what about one object with different states?
dario-g