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.
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).