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());
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.
Hello, Nice post. Do you have the changed source code? If so I’d delighted if you could send me.
thank you
@Higor I have uploaded the source code here:
http://guild3.com/files/samples/EFPoco.zip
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!
@Scott Wade
Awesome. I am looking forward to using InjectionFactory.
??????????????