DotNetCurry Logo

Entity Framework 6 beta – Customizing Code First Conventions

Posted by: Suprotim Agarwal , on 8/22/2013, in Category Entity Framework
Views: 49939
Abstract: Entity Framework 6 (currently in Beta) amongst some shiny new features contains an enhancement that helps you customize Code First Conventions

Entity Framework is now well established as Microsoft’s de-facto data access story. It has notched up 5 impressive revisions already and version 6 started of with a bang in terms of development story. Microsoft open sourced Entity Framework (version 6 onwards), the development code was available on CodePlex, they started accepting pull requests from the community and everyone had access to a nightly build as well as meeting and design notes. While this is a laudable effort given the flak MS has drawn in the past, the reassuring part about the new Development Model was that, releases were still controlled by Microsoft (much like the ASP.NET MVC framework) and the same Support Agreements as of version 5 held true for Enterprise customers who may have had concerns with the new Model.

But today, we are not going to discuss the pros and cons of EF going OSS. Let’s get started and see what new customizations we can do on top of Code First conventions.

 

Customizing Custom Code First Conventions

This implies that we now have greater flexibility in modeling our Databases. Earlier, EF Code first, followed certain naming conventions and derived DB Names/Column Names etc. from its pre-set conventions. For example the Primary Key was expected to be EntityName+’Id’ or just ‘Id’. We could always override them by putting the override via attributes wherever we wanted. But if we wanted to do bulk overrides, we had no option but copy paste. Boring!

Let’s look at an example. Lets say we have a Model class called Post for storing Blog Posts in an ASP.NET MVC Application. Mind you we are using Visual Studio 2013 Preview so it uses the EF Beta by default. If you are using older version of VS, make sure you are using .NET Framework 4.5 and download the latest package from Nuget using

install-package EntityFramework -pre

The –pre tells Nuget to look for pre-release versions and get the latest one around.

Our Post entity has the following structure

public class Post
{
public int PkPostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}

Now as we can see, it has a weird Key with a Pk prefix. Well that’s what our DBA will permit so that’s what it’s gonna be. Imagine this across a more elaborate schema and adding the [Key] attribute for it is not a very enticing proposition.

Thanks to the new capabilities in Entity Framework 6, we can now ‘tell’ EF that all properties starting with Pk are going to be Primary Key columns.

To do this, we have to override the OnModelCreating method in our DbContext. But we haven’t created one yet!

Since we started off with an ASP.NET MVC application, we can usually build the application and then scaffold a new PostsController that uses EntityFramework. This will generate the DB Context, which in case of this example is called EF6WhatsNewContext.

scaffold-controller

But in our case, as soon as we hit Add, we’ll get the following! Oops, if you are not following EF specified default conventions, no cookies for you from the scaffolder.

entity-framework-scaffold-error

So we’ll reset the PK to PostId and Scaffold the Controller. Then change the ID back to PkPostId. Don’t forget to update the scaffolded views to use PkPostId in the Html Helpers as well, else you’ll get errors at runtime.

Customizing Naming Conventions for Primary Keys

We setup our Custom EF6WhatsNewContext as follows:

public class EF6WhatsNewContext : System.Data.Entity.DbContext
{
public EF6WhatsNewContext() : base("name=EF6WhatsNewContext")
{
}
public DbSet<EF6WhatsNew.Models.Post> Posts { get; set; }
 
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder
  modelBuilder)
{
  modelBuilder.Properties().Where(p => p.Name.StartsWith("Pk"))
  .Configure(p => p.IsKey());
}
}

Apart from the standard constructor and the DbSet property for Post, we have overridden the OnModelCreating method. Here from the modelBuilder, we’ve pulled out all the Properties() in the model. This Properties() API is new.

On this list of properties, we’ve applied a Where clause that gives us the reflected details of the Property/Column name. We picked the Name property and checked to see if it starts with Pk.

Once we have all the properties, we use the Configure method and define the property is the key by calling the IsKey() function.

If we build the application now and try to scaffold, it will succeed. If we run the application now and navigate to the /Posts page, we’ll see an empty Index page.

index-page-first-load

Add a row of data and then use SQL Server Explorer to explore the DB Structure

db-schema-max-length

As we can see here, the PkPostId is now setup as Primary Key.

Customizing Column Sizes

From the above diagram however, we see that the Title and Content are both set to nvarchar(max). We know our DBA is going to throw a hissy fit if they see this. We can devise a convention that says anywhere in the Schema if we have properties like Name, Title, Email the length should be limited to nvarchar(200). Content can be nvarchar(max).

Back in the OnModelCreating method of our DbContext, we add the following Property definition

modelBuilder.Properties<string>()
    .Where(p => p.Name.Contains("Title") ||
           p.Name.Contains("Name") ||
           p.Name.Contains("Email")
          ).Configure(p => p.HasMaxLength(200));

This states that any column name of type string containing the words Title, Name or Email will have a max length of 200.

Now our database is already created so we have to use EF Migrations to setup the changes. In the Package Manager Console, we enter the following to enable the migrations first up

PM> enable-migrations

This will setup the initial state of the DB. Next we’ll ask EF to scaffold out the changes with respect to the new Property rule we added.

PM> add-migration TitleMaxLength

We’ll see EF generates a TitleMaxLength class in the Migrations folder where it has commands to Alter the column’s max length

public partial class TitleMaxLength : DbMigration
{
    public override void Up()
    {
        AlterColumn("dbo.Posts", "Title", c => c.String(maxLength: 200));
    }
   
    public override void Down()
    {
        AlterColumn("dbo.Posts", "Title", c => c.String());
    }
}

To update our DB with this change, we apply the Migration as follows

PM> update-database

Now if we go back to the DB in the Explorer, we’ll see the Max length has been set appropriately.

ef-db-schema-with-pk

Re-configuring Column Names

Imagine Halfway down our project, our DB experts do a DB review and reject the column naming convention altogether. They want column names to be prepended with the table name, all UPPER case and each Word in the name should be separated by underscore. So Post.PkPostId should be POST_PK_POST_ID (Okay, okay that’s a weird and unlikely requirement, just play along to see how we get to it).

Actually we have a solution for this too. Back in the DbContext class we add the following method:

private string GetColumnName(System.Reflection.PropertyInfo propertyInfo)
{
var result = System.Text.RegularExpressions.Regex.Replace(
  propertyInfo.Name,
  ".[A-Z]", m => m.Value[0] + "_" + m.Value[1]);
return result.ToUpper();
}

This method takes in a propertyInfo, uses it’s name to generate a string that separates out the Pascal case (or dot separated) words and joins them with underscore. Finally it converts the whole thing to upper case and returns it.

This method does what we want now let’s apply it. We add the following to the OnModelCreating method

modelBuilder.Properties()
.Configure(p => p.HasColumnName(GetColumnName(p.ClrPropertyInfo)));

This basically updates all column names using the return value of the GetColumnName method. We build the application and scaffold the next Migration. Once we apply the DB changes and peek at the DB, it looks as follows:

db-schema-upper-case-names

Pretty neat! That introduces the Code First Convention features in the upcoming Entity Framework 6. There are tons of more features that we’ll cover as we get closer to final release and beyond.

Conclusion

With the Code First convention enhancements, configuring conventions has become much easier using the Fluent API. This will reduce a lot of repetitive work and a ton of attribute clutter if your DB conventions don’t matchup with EF defaults.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and a new one recently at The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for nine times in a row now. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that represents premium web sites and digital publications comprising of Professional web, windows, mobile and cloud developers, technical managers, and architects.

Get in touch with him on Twitter @suprotimagarwal, LinkedIn or befriend him on Facebook



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by bilal on Thursday, August 22, 2013 9:11 PM
awesome. thanks for sharing.
Comment posted by Arvind on Saturday, August 24, 2013 12:27 PM
why modelBuilder.Entities()is not working in EF 6 RC?
Comment posted by Otsile Earl Kole on Monday, August 26, 2013 12:49 PM
Awesome.

What I did, I reinstalled the EF 6 Beta through Nuget, and it gave me this error: "One or more packages could not be completely unistalled: EF6.0.0-beta1. Restart VS to finish unistall"
Comment posted by Sumit on Wednesday, September 4, 2013 9:16 AM
If you are having trouble with the Beta bits and was to use RC1, simply open Package Management Console and type the following:

PM> update-package EntityFramework -pre

That should update your packages to RC1. The code sample works with RC1 too.

Arvind, apparently we now have the strongly typed Generic counterpart modelBuilder.Entity<T>(...)

Comment posted by Sumit on Wednesday, September 4, 2013 11:49 AM
Here are the release notes for RC1

http://blogs.msdn.com/b/adonet/archive/2013/08/21/ef6-release-candidate-available.aspx