Unity Extension for Entity Framework POCO Configuration, Repository and Unit of Work

In my previous two posts I talk about simple EF4 mappings and some common abstractions I like to work with for my entity persistence operations. In this post I want to take all of the setup code necessary for EF configuration and registration and encapsulate it along with the standard boiler plate IOC container code you might see in an application BootStrapper.

 

A Typical DI Example

Though its pretty typical, I thought it would be good to show an example of how I expect to work with a Repository in my application. The ?End Result? if you will.

The following is how one of my MVC.NET controllers might look (same for domain services, WCF services etc). Dependencies are satisfied via constructor injection when the controller or service is built up by the infrastructure (ControllerFactory or perhaps WCF IInstanceProvider).

public class RosterController
{
   private readonly IRepository<Team> _teamRepository;
 
   public RosterController(IRepository<Team> teamRepository)
   {
       _teamRepository = teamRepository;
   }
 
   public void AddNewPlayer(long teamId, string playerName, string position)
   {
       var team = _teamRepository.First(x => x.ID == teamId);
       team.Players.Add(new Player{ Name = playerName, Position = position });
   }
}

In order to ?wire up? the above example, there is quite a bit that has to happen.

 

The Unity Extension

Unity provides extension points, called UnityContainerExtension?s which are a perfect place to handle IOC initialization for a given component. For those familiar with Castle Windsor facilities, its kind of like that (or StructureMap registries).

The EFRepositoryExtension (could probably use a better name?) handles:

  • Creating the ContextBuilder and registering it with the Container
  • Registering the Generic Repository<T> and UnitOfWork
  • Context Lifetime
  • Entity Configuration & EntitySet Registration
  • Context BuildUp

The end result is nice clean extension I can register in my BootStrapper like so:

var container = IoC.Container; //some static, thread safe container class
 
container.AddNewExtension<EFRepositoryExtension>();
container.Configure<IEFRepositoryExtension>()
         .WithConnection(cnxString)
         .WithContextLifetime(new HttpContextLifetimeManager<IObjectContext>())
         .ConfigureEntity(new TeamConfiguration())
         .ConfigureEntity(new PlayerConfiguration());

A pretty nice API to work with if I get to say so 😉
 
Fluent Interface
public interface IEFRepositoryExtension : IUnityContainerExtensionConfigurator
{
    IEFRepositoryExtension WithConnection(string connectionString);
    IEFRepositoryExtension WithContextLifetime(LifetimeManager lifetimeManager);
    IEFRepositoryExtension ConfigureEntity<T>(EntityConfiguration<T> config);
    IEFRepositoryExtension ConfigureEntity<T>(EntityConfiguration<T> config, string setName);
}

Extension Implementation
public class EFRepositoryExtension : UnityContainerExtension, IEFRepositoryExtension
{
  private ContextBuilder<ObjectContext> _builder;
  private SqlConnection _connection;
 
  protected override void Initialize()
  {
      _builder = new ContextBuilder<ObjectContext>();
      //register the builder instance as a singleton, this will hold all of our 
      //mapping information for the duration of our application as it creates 
      //new data contexts
      Container.RegisterInstance("builder", _builder, 
                                 new ContainerControlledLifetimeManager());
 
      //Register Repo & UOW. Those these are transient instances, they both take
      //a ctor dependency on the ObjectContext which has its lifetime controlled
      //by the Extension. E.g., for an Http current request, all repository and
      //UOW will use the same context/transaction
      Container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
      Container.RegisterType<IUnitOfWork, UnitOfWork>();
  }
 
  public IEFRepositoryExtension WithConnection(string connectionString)
  {
      _connection = new SqlConnection(connectionString);
      return this;
  }
 
  public IEFRepositoryExtension ConfigureEntity<T>(EntityConfiguration<T> config)
  {
      //simple pluralization of the entity set
      ConfigureEntity(config, typeof(T).Name+"s");
      return this;
  }
 
  public IEFRepositoryExtension ConfigureEntity<T>(EntityConfiguration<T> config, 
                                                   string setName)
  {
      //add the configuration
      _builder.Configurations.Add(config);
      //register the set metadata
      _builder.RegisterSet<T>(setName);
      return this;
  }
 
  public IEFRepositoryExtension WithContextLifetime(LifetimeManager lifetimeManager)
  {
      Container.AddNewExtension<StaticFactoryExtension>();
      Container.Configure<IStaticFactoryConfiguration>()
               .RegisterFactory<IObjectContext>(x => 
                   ContextResolver(x, lifetimeManager, _connection));
      
      return this;
  }
 
  //factory func to build context with given lifetime & connection
  static readonly Func<IUnityContainer, LifetimeManager, SqlConnection, object> 
      ContextResolver = (c, l, s) =>
      {
          var context = l.GetValue();
          if (context == null)
          {
              var builder = c.Resolve<ContextBuilder<ObjectContext>>("builder");
              var newContext = builder.Create(s);
              context = new ObjectContextAdapter(newContext);
              l.SetValue(context);
          }
 
          return context;
      };
}

 

The Test

Doing simple service location & using TestLifetime, my prototype (not really a ?test? I suppose)looks like:

var cnxString = ConfigurationManager.ConnectionStrings["Test"].ConnectionString;
var container = IoC.Container; //some static, thread safe container class
 
container.AddNewExtension<EFRepositoryExtension>();
container.Configure<IEFRepositoryExtension>()
    .WithConnection(cnxString)
    .WithContextLifetime(new TestLifetimeManager<IObjectContext>())
    .ConfigureEntity(new TeamConfiguration())
    .ConfigureEntity(new PlayerConfiguration());
 
container.RegisterType<RosterController>();
 
var rosterController = container.Resolve<RosterController>();
 
rosterController.AddNewPlayer(3, "Austin Collie", "");
 
container.Resolve<IUnitOfWork>().Commit();

 

Thoughts and feedback are welcome.

6 thoughts on “Unity Extension for Entity Framework POCO Configuration, Repository and Unit of Work

  1. Thanks for the great post…came in very handy to clarify a few things I was fumbling around with prior to running across this clearly explained post.

    With the Beta 1 release of Unity 2.0, I’ve found that the WithContextLifetime method in the EFRepositoryExtension can utilize the new automatic factories functionality…and with an InjectionFactory be refactored into:

    public IEFRepositoryExtension WithContextLifetime(LifetimeManager lifetimeManager)
    {
    Container.RegisterType(new InjectionFactory(x => ContextResolver(x, lifetimeManager, _connection)));

    return this;
    }

    I’ve had success thus far using the first beta of Unity 2.0, and look forward to utilizing it’s expanded features along with greatly enhanced and expanded EF4 and on-going CTPs…

    Thanks again for your posts, keep ’em coming!

Comments are closed.

Proudly powered by WordPress | Theme: Code Blog by Crimson Themes.