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.
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).
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://msdn.microsoft.com/en-us/library/system.data.metadata.edm.edmproperty.aspx ]
5 thoughts on “Entity Validation and the Entity Framework – Part 1”
The link to your T4 template is broken. It points to a file on your hard drive:
file:///C:/Users/Rob/AppData/Local/Temp/WindowsLiveWriter1286139640/supfilesE46443/Model.tt
Ah sorry, had some issues publishing that particular article. Live Writer fail and all that. Fixed now.
Abstract clasc EntityBase not found in the class generated for the template t4
Hi Alex,
I’m using a custom base class. Just remove the reference to ‘EntityBase’ in the T4 template and it should work fine.
R
OK, I’ve uploaded an edited copy and commented out the base class reference – give it a try now..