views:

263

answers:

1

I have two classes, for this example, that are partial classes against LINQ-to-SQL model classes.

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

    public IEnumerable<RuleViolation> GetRuleViolations() 
    {
        yield break;
    }

    partial void OnValidate(ChangeAction action) 
    {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }
}

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

    public IEnumerable<RuleViolation> GetRuleViolations() 
    {
        yield break;
    }

    partial void OnValidate(ChangeAction action) 
    {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }
}

I'd like to factor this functionality out to remove the redundant logic. I tried it with an IModel interface and then extension methods for contracts but it broke down with the partial class.

I ended up with this:

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

        public void OnValidate(ChangeAction action)
        {
            if (!IsValid) throw new ApplicationException("Rule violations prevent saving");
        }

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

public partial class Blog : ModelBase
{
    partial void OnValidate(ChangeAction action)
    {
        base.OnValidate(action);
    }

    public override IEnumerable<RuleViolation> GetRuleViolations()
    {
        // rules omitted
    }
}

Should I do this another way? Thank you.

+1  A: 

The only thing that I would add to this is that you might want to define an interface, then various base classes implementing the interface to provide some default validation. For example, you might have a base class that does XSS validation of string properties, verifying that they don't contain any HTML. Using an interface will allow you to derive from any of these base classes (or even just the interface, if you want) and still be able to treat them as the interface. You might also consider having a signature that allows you to specify the ChangeAction -- you might have different validation rules for Delete than Update or Insert.

public interface IValidatedEntity
{
    IEnumerable<RuleViolations> GetRuleViolations();
    IEnumerable<RuleViolations> GetRuleViolations( ChangeAction action );
}

public abstract class XSSValidatedEntity : IValidatedEntity
{
    public virtual IEnumerable<RuleViolations> GetRuleViolations()
    {
        return GetRuleViolations( ChangeAction.Insert );
    }

    public virtual IEnumerable<RuleViolations> GetRuleViolations( ChangeAction action )
    {
         if (action != ChangeAction.Delete)
         {
             return ValidateStringProperties();
         }
         return new List<RuleViolations>();
    }
}
tvanfosson
"you might have different validation rules for Delete than Update or Insert", nice
blu