views:

133

answers:

3

I have an IEntity interface that implements an interface, IValidatable

public interface IValidatable {
    bool IsValid { get; }
    bool IsValidForPersistence { get; }
    // Rules applied at UI time (please enter your name, etc)
    IEnumerable<RuleViolation> GetRuleViolations();
    // Rules to be applied only at persistence time
    IEnumerable<RuleViolation> GetPersistenceRuleViolations();
}

public interface IEntity : IValidatable {
    int ID { get; set; }
}

and for convenience sake I've implemented my Entity class like:

 public abstract class Entity : IEntity {

        public virtual int ID { get; set; }

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

        public virtual bool IsValidForPersistence {
            get { return PersistenceRuleViolations().Count() == 0; }
        }

        public virtual IEnumerable<RuleViolation> GetRuleViolations() {
            return new List<RuleViolation>();
        }

        public virtual IEnumerable<RuleViolation> GetPersistenceRuleViolations() {
            return new List<RuleViolation>();
        }
    }

By default entities are valid, until GetRuleViolations() or GetPersistenceRuleViolations() are overridden.

  public partial class Company {

      public override IEnumerable<RuleViolation> GetRuleViolations() {
         if (String.IsNullOrEmpty(CompanyName))
                yield return new RuleViolation("CompanyName", "Name is required.");
     }

      public override IEnumerable<RuleViolation> GetPersistenceRuleViolations() {
         // Include standard rules too
         foreach (RuleViolation rule in RuleViolations) {
              yield return rule;
          }
        // Check some data based on a referenced entity "Bid"
        if (!Active && Bid.Active)
            yield return new RuleViolation("Active", 
               "When Active is set to false, the Bid must also be inactive.");
     }
  }

I know this is a bit naive for validation, so besides any typos, what can be improved?

+1  A: 

I'd recommend looking at either the NCommon framework which has a good business rule and validation base framework, or the xVal validation framework, which also supports client side validation from the same ruleset.

Logic Labs
A: 

I'm using ASP.NET MVC and doing something similar with Data Annotation Validators. I inherit however from ValidationAttribute and also override FormatErrorMessage so that I can return the errors collectively.

Nissan Fan
+1  A: 

Keep in mind, if you derive an entity from another, eg. Customer from Person, you need a foeach to get the base classes violations:

  public override IEnumerable<RuleViolation> GetRuleViolations() 
  {
     // inherit base class valiations
     foreach (var violation in base.GetRuleViolations())
     {
         yield return violation;
     }

     // add own validations
     if (String.IsNullOrEmpty(CompanyName))
         yield return new RuleViolation("CompanyName", "Name is required.");
  }

Which is not so nice.

Personally, I would also look for something existing, because validation is a general problem and there are already many solutions for it.

Stefan Steinegger
Do you have any specific recommendations?
cadmium
The validation solution highly depends on the technologies and infrastructure you already have. There is for instance a validation framework of NHibernate, or one by Microsoft (an application block), or by some GUI library vendors and many more. I think, there is no general advice.
Stefan Steinegger