Entity Validation and the Entity Framework – Part 1 5


Introduction

The next logical step in my journey across the Entity Framework v6 RC1 after having established a fairly robust data access approach was to imbue my solution with some more “smarts” when it came to entity validation.

Out-of-the-box Entity Framework POCO entities are exactly as advertised – plain old CLR objects – and, as such, do not contain any inherent validation hints or help.

There are a plethora of validation approaches available, including extending the entity classes with partial classes, but I wanted something implemented which would be core to the data access approach – and (because I’m lazy) something that didn’t require any hand written code, outside of modifying a template.  After all, the EDMX already knows about nullable fields and field lengths, right?  Why not take advantage of that knowledge?

To be clear – this is not an approach designed to validate entities in an interactive fashion as there are other approaches which would work better, including UI validation.  Instead, this is an implementation designed to catch (and reject) any inserts or updates which would genuinely fail if an attempt to commit the changes was made – i.e. this is a last ditch sanity check.

Interrogating the T4 template

To accomplish what I want to do, you’ll have to be comfortable hand editing the T4 template which is used to generate the Entity Framework POCO entities.  If you haven’t done a lot of templating before, this might seem a bit daunting at first.

Untitled_thumb[2]

If you expand the <DataModel>.edmx file, you’ll see there are a host of linked dependency files.  The one we’re interested in specifically is called Model.tt and it is a direct dependency of the EDMX file itself.

There’s a fairly obvious format to the template, and you can actually play with the template and see the results.  Every time you save the template it triggers the templating engine – but be warned, any errors will break the template and you’ll get an error message.

An Example Schema Object

Let’s look at an example from my previous data schema.  As you can see, nothing terribly scary here, but there are a couple of required fields (not-nullable) and some fields which have defined lengths (we’re focusing on strings here).

image_thumb5

What I’m aiming to produce are some Data Annotations decorating the appropriate properties on my corresponding Catalog data entity, which would look like this (apologies for the text wrapping):

// Simple Properties

[Required(ErrorMessage="[Property 'CatalogId' in entity 'Catalog' is a required property.]")]
public int CatalogId { get; set; }
        
[StringLength(100, ErrorMessage="[The field 'Title' in entity 'Catalog' cannot be longer than 100 characters.]")]
[Required(ErrorMessage="[Property 'Title' in entity 'Catalog' is a required property.]")]
public string Title { get; set; }
        
[StringLength(400, ErrorMessage="[The field 'Description' in entity 'Catalog' cannot be longer than 400 characters.]")]
public string Description { get; set; }
public Nullable<System.DateTime> DateTaken { get; set; } [StringLength(50, ErrorMessage="[The field 'OriginalFilename' in entity 'Catalog' cannot be longer than 50 characters.]")] public string OriginalFilename { get; set; }
public Nullable<int> Orientation { get; set; } // End Simple Properties

 

To accomplish this I also need the class implementation to use the following namespace:

using System.ComponentModel.DataAnnotations;

Modifying the T4 Template

There are two key changes which need to be made.  The first is to insert the namespace – which I’ve done in a less than graceful manner by just dropping it into the template right after the BeginNamespace call:

foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>
(itemCollection)) { fileManager.StartNewFile(entity.Name + ".cs"); BeginNamespace(code); #> <#=codeStringGenerator.UsingDirectives(inHeader: false)#>
using System.ComponentModel.DataAnnotations; <#=codeStringGenerator.EntityClassOpening(entity)#> : EntityBase {

 

Two things to note here – in bold – one is the base class I added previously, and the second is the DataAnnotations namespace.  Next, we’ll add the logic to prefix properties with the annotations we desire for validation.

You’re looking to move beyond the default constructor and find a section which looks like this:

foreach (var edmProperty in simpleProperties)
{

 

This section handles generation of (simple) properties for the entity.  There are two main conditions we want to check for – non-nullable properties (required fields) and fields with a fixed length.  Here is the full (modified) code for the section of the T4 template which generates simple properties:

Note: I started writing this by hand until found this problem  solved in the following article, but I modified the annotations to provide a bit more context (including the property names and source entity type).  What you see here is more or less copied from the linked article.

var simpleProperties = typeMapper.GetSimpleProperties(entity);
if (simpleProperties.Any())
{
foreach (var edmProperty in simpleProperties)
{
// begin max length attribute
if (code.Escape(edmProperty.TypeUsage) == "string")
{
int maxLength = 0;
if (edmProperty.TypeUsage.Facets["MaxLength"].Value != null && 
    Int32.TryParse(edmProperty.TypeUsage.Facets["MaxLength"].Value.ToString(), out maxLength)) { #> [StringLength(<#=code.CreateLiteral(maxLength)#>,
    ErrorMessage="[The field '<#=edmProperty.Name#>' in entity '<#=entity.Name#>' cannot be longer than <#=code.CreateLiteral(maxLength)#> characters.]")] <# } } // begin required attribute if (edmProperty.TypeUsage.Facets["Nullable"].Value.ToString() =="False") { #> [Required(ErrorMessage="[Property '<#=edmProperty.Name#>' in entity '<#=entity.Name#>' is a required property.]")] <# } #> <#=codeStringGenerator.Property(edmProperty)#> <# } } #>

 

Again, apologies for the wrapped and poorly indented code – limitations of this blogging format unfortunately.  I’ve included my copy of my model.tt below so you can get a properly formatted version.  Code in italics is original non-modified template code.

The Outcome

This produces the following generated entity – with data annotations:

// Simple Properties
[Required(ErrorMessage="[Property 'CatalogId' in entity 'Catalog' is a required property.]")]
public int CatalogId { get; set; }
        
[StringLength(100, ErrorMessage="[The field 'Title' in entity 'Catalog' cannot be longer than 100 characters.]")]
[Required(ErrorMessage="[Property 'Title' in entity 'Catalog' is a required property.]")]
public string Title { get; set; }
        
[StringLength(400, ErrorMessage="[The field 'Description' in entity 'Catalog' cannot be longer than 400 characters.]")]
public string Description { get; set; }
public Nullable<System.DateTime> DateTaken { get; set; }
        
[StringLength(50, ErrorMessage="[The field 'OriginalFilename' in entity 'Catalog' cannot be longer than 50 characters.]")]
public string OriginalFilename { get; set; }
public Nullable<int> Orientation { get; set; }
// End Simple Properties

 

Summary

We’re not done just yet.. but this is a start.  In part 2, I’ll be showing how this effort is put to use, by integrating it into my previously documented data access approach.  Continued in Part 2.

My Template

Here’s my [ T4 Template ]

Here’s the template without the base class reference

Massively Helpful

[ http://blogs.msdn.com/b/lgmorand/archive/2010/12/31/entity-framework-template-t4-and-data-annotations.aspx ]

[ http://msdn.microsoft.com/en-us/library/system.data.metadata.edm.edmproperty.aspx ]


About Rob Sanders

IT Professional and TOGAF 9 certified architect with nearly two decades of industry experience, 18 years in commercial software development and 11 years in IT consulting. Check out the "About Rob" page for more information.


Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

5 thoughts on “Entity Validation and the Entity Framework – Part 1