Customizing ‘Reverse Engineer Code First’ in the EF Power Tools

Beta 2 of the Entity Framework Power Tools was recently released. By far the most popular feature out of these power tools is the ability to reverse engineer a Code First model from an existing database.

Admittedly ‘Code First’ wasn’t the best choice of names… Code First is really just a code-based alternate to the EF Designer, it supports creating a new database or mapping to an existing database. Using Code First against an existing database is often called ‘Code Second’ because… well… you have the database first and then write the code second.

I’ve included a download of the modified templates at the end of this post.

 

Reverse Engineer Code First

If you are already familiar with ‘Reverse Engineer Code First’ then skip ahead to the next section.

You’ll need the EF Power Tools installed from Visual Studio Gallery.

You start by right clicking on a project and selecting Entity Framework –> Reverse Engineer Code First:

ReverseEngineerCodeFirst

 

Then You’re prompted for details of the database you want to use:

SpecifyDatabase

 

A derived DbContext, a set of POCO classes and a set of mapping classes will be generated in a Models folder in your project:

GeneratedClasses

 

The Scenario

If you look in the mapping classes (those are the ones in the Mapping folder) you’ll see that ‘Reverse Engineer Code First’ uses the Fluent API for all it’s configuration.

FluentAPI

 

A common request is to use Data Annotations for configuration, rather than the Fluent API. You’ll also see that it generates table\column mapping code for all classes/properties, even though Code First would correctly work most of these out without configuration.

Let’s modify the code generation so that column and table mappings are done with Data Annotations and are only generated when required.

 

Adding the Reverse Engineer Templates

Right click on your project and select Entity Framework –> Customize Reverse Engineer Templates:

Customize

 

This will add a set of T4 templates to your project. The Power Tools will use these templates to generate the code for the various classes it adds to your project.

Templates

 

Note: You may see the following error after you add the templates. This is safe to ignore. It’s a result of Visual Studio trying to validate the templates… but they are never run from your project.

Compiling transformation: The type or namespace name ‘EfTextTemplateHost’ could not be found (are you missing a using directive or an assembly reference?)

 

Removing Fluent API Table/Column Mapping

The first step is to stop generating table and column mapping in the mapping classes. This is the easy part, in Beta 2 of the Power Tools just delete line 126 thru 152 (inclusive) from the Mapping.tt file.

Mapping

 

Adding Data Annotation Mapping

Now it’s time to edit Entity.tt to include the [Column] and [Table] attributes where required.

Let’s start by generating a [Column] attribute for any properties whose name doesn’t match the column that they map to.

This can happen when the column name contains characters that aren’t supported by C#. For example the column name may have a space, the reverse engineer process will generate a property that uses an underscore instead of a space. If we don’t supply any mapping then Code First will look for a column with an underscore instead of a space.

In Beta 2 you’ll find the property generation code around line 39 in Entity.tt, add the highlighted section from the code below.

    foreach (var property in efHost.EntityType.Properties)
{

                var columnName = efHost.PropertyToColumnMappings[property].Name;
if(code.Escape(property) != columnName)
{
#>
[Column(“<#= columnName #>”)]
<#
}

#>
<#= Accessibility.ForProperty(property) #> <#= code.Escape(property.TypeUsage) #> <#= code.Escape(property) #> { get; set; }
<#
}

 

Next we need to generate a [Table] attribute when the pluralized name of the class doesn’t match the table it maps to.

This can happen if the table name is not pluralized in the database. For example, if you have a table named Category then the reverse engineer process will generate a class called Category. If you don’t supply any configuration then Code First will look for a table called Categories in the database. We also need to specify configuration if the table is not in the dbo schema.

In Beta 2 you’ll find the code that generates the class definition line around line 13 in Entity.tt, add the highlighted section from the code below.

namespace <#= code.EscapeNamespace(efHost.Namespace) #>
{
<#
var tableName = (string)efHost.TableSet.MetadataProperties[“Table”].Value ?? efHost.TableSet.Name;
var conventionTableName = System.Data.Entity.Design.PluralizationServices.PluralizationService
.CreateService(new CultureInfo(“en”))
.Pluralize(efHost.EntityType.Name);

        var schemaName = (string)efHost.TableSet.MetadataProperties[“Schema”].Value;
schemaName = string.IsNullOrWhiteSpace(schemaName)
? “dbo”
: schemaName;

        if(schemaName != “dbo” || conventionTableName != tableName)
{
#>
[Table(“<#= tableName #>”, Schema=”<#= schemaName #>”)]
<#
}

#>
   public class <#= efHost.EntityType.Name #>
{
<#

 

The final step is to modify the template to generate a using for the namespace that the Data Annotations reside in. Add the highlighted code under the existing usings (around line 10 in the Beta 2 version of Entity.tt).

The namespace for the Table/Column annotations changed after EF4.3, so we check which version we are using and generate the correct namespace.

using System;
using System.Collections.Generic
;
<#
if (efHost.EntityFrameworkVersion >= new Version(4, 4))
{
#>
using System.ComponentModel.DataAnnotations.Schema;
<#
}
else
{
#>
using System.ComponentModel.DataAnnotations;
<#
}
#>

 

The Outcome

Now that we’ve made the edits you can re-run the reverse engineer process – make sure you save the changes to the tt files before you run it.

The table/column mapping code is now omitted from the configuration classes:

CleanFluentAPI

 

Where the mapping doesn’t line up with the Code First conventions we are now generating data annotations in the classes themselves:

DataAnnotationMapping

 

But if everything lines up then we don’t generate any mapping at all:

NoMapping

 

Conclusion

Here are the templates if you want to download them. It would be pretty simple to move specification of primary key etc. to use data annotations as well. You are welcome to edit and redistribute the templates as you see fit Smile