Wednesday, December 22, 2010

Entity Framework (EF4 Code First): Adding Simple Support For Enums

The other day I decided to spend a bit of time upgrading my development environment by installing RC2 of MVC3 and applying the VS2010 SP1 Beta.  I then decided to test out the new Razor syntax by developing a small application using my upgraded environment.

As with all new data driven projects, one of the things that I needed to do was to choose my ORM technology - and this time I really wanted to try out the new EF4 Code First stuff to get my knowledge up to date with that technology.

As you can see from ScottGu's blog post, using EF4 Code First is a breeze; create your classes, add a DbContext class, and away you go!  However in my case, this was not quite that simple because I had decided to model one of my class properties as an enum in the middle-tier code.

That is, I wanted to have an entity modeled int the database the following way:
And have it modeled in the middle-tier like so:

public class Participant 
{
    public int ParticipantId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }
    public Gender Gender { get; set; }
    public DateTime DateOfBirth { get; set; }
}

According to articles I've read, the current build of EF does not natively support mapping to Enum's but that this is a feature which is on the product roadmap for inclusion in the near future.  In the meantime, the best article that I could find to work out how to implement support for my scenario was a blog post written by a Program Manager working on the Data Services team at Microsoft.

There were 2 things that I didn't like about that approach:

  1. The article mixed metaphors between code first syntax and EDMX-style syntax
  2. I couldn't get it to work!
I showed @jburger and he suggested a much simpler solution which I thought I'd share here.  His solution was to simply map an integer property directly from the middle-tier object to the database and to wrap that with a calculated property which contains the mapping logic to do the conversion.  Here's the solution that I coded up:

public class Participant 
{
    public int ParticipantId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }


    [Column(Name="Gender")]
    public int InternalGender { get; set; }
    [NotMapped]
    public Gender Gender
    {
        get { return (Gender)this.InternalGender; }
        set { this.InternalGender = (int)value; }
    }


    public DateTime DateOfBirth { get; set; }
}

As you can see, this implementation uses a convention of naming the EF mapped property with the suffix of "Internal" and using a data annotation to force EF to understand that it is the mapped column.  On the property that we want to expose to our middle-tier code by simply apply a NotMapped attribute.

For the purpose of completeness, I believe that the outcomes that are achieved through applying these attributes could also be achieved through a coded approach like so:

Ignore the wrapped property:
modelBuilder.Entity<Participant>()
    .Ignore(p => p.Gender); 
 Change the mapping of the property to the column name:
modelBuilder.Entity<Participant>()
    .Property(p => p.InternalGender)
    .HasColumnName("Gender");