Monday, January 7, 2013

Entity Framework & StructureMap

Entity Framework and StructureMap, weird combo, yeah I know. But here is a "cute" little trick if you use Code First and roll your own entity config classes. I started with an empty solution, added a data project and a console project.

In the data project, a simple POCO:

public class AboutUs
{
   public virtual long DealerId { get; set; }
   public virtual short ImageOrder { get; set; }
   public virtual string Url { get; set; }
   public virtual string Alt { get; set; }
   public virtual string Size { get; set; }
}
 
Now let's set up the DbContext...

using System.Data.Entity;
using EF5Demo.Data.Entities;

public class EF5DemoContext : DbContext
{
   public EF5DemoContext()
   {
      this.Configuration.AutoDetectChangesEnabled = true;
      this.Configuration.ProxyCreationEnabled = true;
   }

   public DbSet AboutUs { get; set; }

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {

   }
}
 
We'll fill out the OnModelCreating after we drop in StructureMap. For this little setup, StructureMap will be needed in both projects. In each project, right click on References, select Manage NuGet Packages. We just need the regular version of StructureMap.
 
 
 
Now to set up an interface and a concrete implementation that will be consumed by the entity config classes. First, the IEntityConfiguration interface:

using System.Data.Entity.ModelConfiguration.Configuration;

public interface IEntityConfiguration
{
   void AddConfiguration(ConfigurationRegistrar registrar);
}
 
The class that uses this interface is the link between StructureMap and the entities, the ContextConfiguration class:
 
using System.Collections.Generic;

public class ContextConfiguration
{
   public IEnumerable Configurations
   {
      get { return ObjectFactory.GetAllInstances(); }
   }
}
 
Let's roll the entity config class for the AboutUs POCO.This config class will implement the IEntityConfiguration interface:
 
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.ComponentModel.DataAnnotations.Schema;
using EF5Demo.Data.Entities;

public class AboutUsConfig : EntityTypeConfiguration, IEntityConfiguration
{
   public AboutUsConfig()
   {
      // composite PK
      HasKey(aui => aui.DealerId, aui.ImageOrder });
      // Identity column
      Property(aui => aui.DealerId).HasColumnName("DealerId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
      Property(aui => aui.ImageOrder).HasColumnName("ImageOrder");
      Property(aui => aui.Url).HasColumnName("Url").IsRequired();
      Property(aui => aui.Alt).HasColumnName("Alt").IsRequired();
      Property(aui => aui.Size).HasColumnName("Size").IsRequired();
      ToTable("AboutUs");
   }

   public void AddConfiguration(ConfigurationRegistrar registrar)
   {
      registrar.Add(this);
   }
}
 
Let's return to the DbContext class and fill out the OnModelCreating method...
 
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   ContextConfiguration ctxConfig = new ContextConfiguration();

   foreach (var config in ctxConfig.Configurations)
   {
      config.AddConfiguration(modelBuilder.Configurations);
   }
}
 
When the model gets created, the ContextConfiguration class gets created. The Configurations property looks for all instances of the IEntityConfiguration interface (which would be all the entity config classes) and adds them to the model builder, and thus, provide structure and validation to your db tables.
 
Let's see if it works, in the console project...

using StructureMap;
using EF5Demo.Data;

static void Main(string[] args)
{
   ObjectFactory.Initialize(x =>
   {
      {
         scan.TheCallingAssembly();
         scan.WithDefaultConventions();
         scan.Assembly("EF5Demo.Data");
         scan.AddAllTypesOf();
      };
   });

   ContextConfiguration configurations = 
      ObjectFactory.GetInstance();

   foreach (var configuration in configurations.Configurations)
   {
      Console.WriteLine(configuration);
   }
}
 

There we go, entity config classes have been picked up. Like I said, this is just a cute little trick. The benefit is that for every entity config class you add, you don't have to go back and add it to the model builder. Conversely, whenever you remove an entity you won't have to worry about removing the config from the model builder.