Validation With Entity Framework

This is a topic I spent an entire weekend working on while developing Code Tunnel because it is very poorly documented and little information exists regarding it.

A Little Background

Entity Framework (EF) is an Object Relational Mapper (ORM) which auto-generates entities based on a provided data source. Actually the truth is that EF is capable of much more than that, but that is its basic and most common usage. If you're familiar at all with LINQ to SQL (L2S) then EF is not that hard to learn. One thing L2S had over EF is the ability to use model validation via validation attributes.

Model validation is the practice of applying validation rules to your model objects, rather than applying them to your user interface directly. What this does is ensure that no matter what happens in the business layer validation will be enforced at the model level, preventing a buggy UI from ever making a mistake and saving out an invalid model object. This technique is also very DRY and is easily maintained.

The Problem

The problem with model validation and Entity Framework comes in the fact that (most of the time) your entites are auto-generated. What this means is that you no longer have direct access to your model objects. Sure, you could open up the auto-generated code and slap on a [Required] attribute, but the next time you updated your model it would get completely overridden and suddenly disappear. We can't have that.

The Solution

EF is very robust and they definitely thought of this shortcoming. There are three possible options for validation in EF; I will list them in the order that I tried them:

1) EF contains partial methods that are auto-generated with your entities. Here is a trimmed-down sample of the code generated by EF for the Code Tunnel BlogPost entity:

public partial class BlogPost : EntityObject  
{
    [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
    [DataMemberAttribute()]
    public global::System.String Title
    {
        get
        {
            return _Title;
        }
        set
        {
            OnTitleChanging(value); // <--- pay special attention to this method call.
            ReportPropertyChanging("Title");
            _Title = StructuralObject.SetValidValue(value, false);
            ReportPropertyChanged("Title");
            OnTitleChanged();
        }
    }
    private global::System.String _Title;
    partial void OnTitleChanging(global::System.String value);
    partial void OnTitleChanged();
}

The code above as I said, is auto-generated. Every time you update your model this code is re-created. So adding any validation attributes to the object is pointless. Notice the comment I made, telling you to pay special attention to the OnTitleChanging() method call. This call is to a partial method that you can see below the property is only defined and nothing more. It is because of this you can implement the partial method and do some handling of your own before the property actually gets changed. All you have to do is create a partial class with the same name as your entity and a partial method within that class to extend the auto-generated partial method. Then you throw a validation exception when the value is not desired:

public partial class BlogPost  
{
    partial void OnTitleChanging(string value)
    {
        if (string.IsNullOrEmpty(value))
            throw new ValidationException("Title is required.");
    }
}

This is very succinct code and I was happy that it existed. All was right in the world.....................nevermind. Turns out that in ASP.NET MVC the ModelState dictionary does not read the message specified in the ValidationException. This is odd because in regular ASP.NET the dynamic validator control does read the message generated by validation exceptions. This does invalidate the model, but the message displayed by the validation helper will just be "The value 'value' is invalid". This is definitely not desirable if you care about not confusing your visitors. Unfortunately, this partial method returns void and there isn't much else we can do to get this custom message to bubble up to the UI so it's on to option two.

2) After failing to get the validation message to bubble up to the UI via the ValidationException I discovered that the MVC model state checks if the entity inherits from IDataErrorInfo which is an old interface that has been around since .NET 1.1. It's a simple interface to implement.

public partial class BlogPost : IDataErrorInfo  
{
    private Dictionary<string, string> _errors = new Dictionary<string, string>();

    partial void OnTitleChanging(string value)
    {
        if (string.IsNullOrEmpty(value))
            _errors.Add("Title", "Title is required.");
    }

    public string Error
    {
        get { return string.Empty; }
    }

    public string this[string columnName]
    {
        get
        {
            if (_errors.ContainsKey(columnName))
                return _errors[columnName];
            return string.Empty;
        }
    }
}

Now that our entity inherits from IDataErrorInfo MVC will check for errors and retrieve their custom error messages to add to ModelState. YAY! All is right in the world...again.................nevermind. There is a small problem with this, of course. This works great for validating almost everything under the sun, except for a required field that is non-nullable. If I have a property on my object that cannot be null, EF throws an exception before it even calls the partial method OnTitleChanging. The exception is because the model property is defined as a non-nullable type and will not allow null to be assigned. A few people suggested making the property nullable so that the exception doesn't occur and the method is called, but this won't work for several reasons:

a) When you have certain relationships defined in your model it will not allow you to set the property to null.

b) If the type is a non-nullable type, like int, then it is simply impossible to set it to null unless you change the type to int? which just cascades into little issues all over your application. No good. On top of those issues I actually found out that Microsoft considers IDataErrorInfo to be backward compatibility and suggests users migrate away from it at their convenience. On to option three.

c) By this time I was getting pretty frustrated. I did not think model validation would be so difficult to implement. I had all but given up and was going to settle on custom validation messages in the UI, :(, when I discovered this little beauty: [MetadataType(typeof(BlogPostMetadata))]. This little attribute is pretty awesome. It is called a Metadata Type Attribute it allows you to specify another class, with the same properties and attach actual validation attributes to them!

[MetadataType(typeof(BlogPostMetadata))]
public partial class BlogPost  
{
}

public class BlogPostMetadata  
{
    [Required(ErrorMessage="Title is required.")]
    public string Title;

    [Required(ErrorMessage="Body is required.")]
    public string Body;
}

This looks a little funny since the initial partial class declaration is empty but who cares, it works! I can now specify validation attributes to my heart's content and the custom messages bubble right up to the UI and the MVC validation helpers like they are supposed to, including the required messages. Finally after all that running around, we have working model validation that correctly bubbles up to the UI so the user can see the message and you only have to define the message once.

Well that was a headache for sure. I'm just glad the resolution was something useful and despite all the trouble it was definitely worthwhile. Entity Framework is still fairly young, but I think they are finally reaching a point where a lot of the rough spots are being ironed out. I hope this article helps someone in my same frustrating position. Happy coding :)

Chev

Read more posts by this author.

comments powered by Disqus