OdeToCode IC Logo

Composing Entity Framework Fluent Configurations

Monday, November 28, 2011

The canonical example for fluent configuration with the Entity Framework is to take a few simple entity definitions:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public byte[] Version { get; protected set; }
}

... and configure them all inside of the DbContext's OnModelCreating method:

protected override void OnModelCreating(
    DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().ToTable("products");
    // ... more configuration

    base.OnModelCreating(modelBuilder);
}

This approach doesn't scale well as we add more entities, so let's try an alternate approach. We'll put the configuration for each entity into a distinct class.

public class ProductConfiguration : EntityTypeConfiguration<Product>
{
    public ProductConfiguration()
    {
        ToTable("products");            
        Property(p => p.Name).HasMaxLength(255);
        Property(p => p.Version)
            .IsConcurrencyToken()
            .IsRowVersion();
    }
}

Now model building is as simple as adding all the configuration objects into the model builder's configuration registrar.

protected override void OnModelCreating(
                            DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new ProductConfiguration());
    // ...  and so on, for each configuration class

    base.OnModelCreating(modelBuilder);
}

The new drawback is someone needs to instantiate every configuration object during OnModelCreating, which is a boring, silly, maintenance concern to worry about when things start to change. The next improvement is to automate this chore. 

Enter MEF

We can use reflection, or any decent composition / IoC container to find all of the  configuration classes in an application and hand them to us in a collection. It's easy to fall into a line of thinking that says we need this collection to be a collection of  EntityTypeConfiguration<TEntity>, because it's easy to picture the model building code like this:

foreach (var configuration in allConfigurations)
{
    modelBuilder.Configurations.Add(configuration);
}

It turns out this approach is not only limiting, it can also be difficult to pull off. Since the TEntity parameter in EntityTypeConfiguration<TEntity> will vary across all the entities, you either end up working with things like open generic types (EntityTypeConfiguration<>) or a closed type of EntityTypeConfiguration<object>. It's ugly and I'll spare you the details. Let's take a different approach that starts with an interface.

public interface IEntityConfiguration
{
    void AddConfiguration(ConfigurationRegistrar registrar);
}

And then a class that will collect (via MEF) all the objects that want to participate in the configuration of a context.

public class ContextConfiguration
{
    [ImportMany(typeof(IEntityConfiguration))]
    public IEnumerable<IEntityConfiguration> Configurations
    {
        get; set;
    }        
}

The individual configuration objects then get decorated with MEF export attributes and implement the IEntityConfiguration interface.

[Export(typeof(IEntityConfiguration))]
public class ProductConfiguration : 
    EntityTypeConfiguration<Product>,
    IEntityConfiguration
{
    public ProductConfiguration()
    {
        ToTable("products");   
        // ... more configuration         
    }

    public void AddConfiguration(ConfigurationRegistrar registrar)
    {
        registrar.Add(this);
    }
}

Then a brute force approach to model building might look like this:

protected override void OnModelCreating(
                            DbModelBuilder modelBuilder)
{
    var contextConfiguration = new ContextConfiguration();
    var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
    var container = new CompositionContainer(catalog);            
    container.ComposeParts(contextConfiguration);

    foreach (var configuration in 
        contextConfiguration.Configurations)
    {
        configuration.AddConfiguration(
            modelBuilder.Configurations);    
    }            
    base.OnModelCreating(modelBuilder);
}

With this approach the code inside OnModelCreating never has to change, even when new entity configurations appear in the application. Also, there are no messy type issues with generic parameters, and the relationship between model building and configurations is a bit more flexible (in a double-dispatch / visitor pattern kind of way).