Supporting multiple databases using the Unit of Work and Repository patterns
By Rob Jones | December 13, 2011
You might have read my previous entries about the Unit of Work and Repository design patterns. If not, you might want to check them out before reading the rest of this article.
- Unit of Work & Repository support for multiple frameworks
- Letting your Unit of Work & Repositories support multiple frameworks
A little while ago, I had to redesign the Unit of Work and Repository structure in a project at work. Naturally, I was quite happy to do so, as I have done a lot of research and development work on this topic already.
A requirement was that the unit of work should be able to work with multiple databases. I figured that the generic approach as described in my previous articles would work just as well to solve this issue. I was rather pleased when I discovered that the sample code, provided as download in one of my previous articles, worked with only a few modifications.
Another thing of beauty – which is slightly off-topic, but deserves to be mentioned – was to see the code working through a different inversion of control container. My code always used Castle Windsor for IoC and DI, but the project at work uses StructureMap. I had a little nerdgasm when I saw my code working in a completely different environment; it really gave me the feeling that I had written some very versatile code that can be implemented in different scenarios with very little effort!
On to the actual code now…
The project uses Entity Framework 4.1 and has no requirements to support different ORM tools. This allowed me to make a few minor code changes to facilitate EF 4.1 a little better.
Let’s start with the unit of work interface:
public interface IUnitOfWork : IDisposable
{
void Commit();
}
/// <summary>
/// Generic unit of work interface for different DbContext objects.
/// </summary>
/// <remarks>
/// Keyword "out" is specified for the generic parameter
/// to allow casting (covariance).
/// </remarks>
/// <typeparam name="TContext">Concrete DbContext type.</typeparam>
public interface IUnitOfWork<out TContext> : IUnitOfWork
where TContext : DbContext
{
TContext Context { get; }
}
Very little changes in the code here. An out-keyword was added to the generic TContext parameter and an added restriction that TContext should always be of type DbContext (Entity Framework 4.1 specific change).
The only other change was done in the abstract Repository class:
public abstract class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
private IUnitOfWork<DbContext> unitOfWork;
private readonly IDbSet<TEntity> entitySet;
/// <summary>
/// Creates a new repository.
/// </summary>
/// <param name="unitOfWork">The unit of work to use.</param>
public Repository(IUnitOfWork unitOfWork)
{
this.unitOfWork = (IUnitOfWork<DbContext>)unitOfWork;
this.entitySet = this.unitOfWork.Context.Set<TEntity>();
}
}
This code was modified to be able to cast the IUnitOfWork to an IUnitOfWork<DbContext>. This is another change specific for EF 4.1 to allow me to access the context and create an entity set.
And finally, the StructureMap registries tie everything together when bootstrapping the application:
ForConcreteType<ConcreteContext>();
ForConcreteType<AnotherConcreteContext>();
ForConcreteType<UnitOfWork<ConcreteContext>>()
.Configure
.Ctor<ConcreteContext>();
ForConcreteType<UnitOfWork<AnotherConcreteContext>>()
.Configure
.Ctor<AnotherConcreteContext>();
For<ISomeRepository>()
.Use<SomeRepository>()
.Ctor<IUnitOfWork>()
.Is<UnitOfWork<ConcreteContext>>();
For<IOtherRepository>()
.Use<OtherRepository>()
.Ctor<IUnitOfWork>()
.Is<UnitOfWork<AnotherConcreteContext>>();
I’m not an expert on StructureMap, so I’m not sure if this is the most efficient way to go, but here I define the concrete context objects for the databases as well as which context to use when initializing the repositories.
Hopefully this, combined with my previous posts, is enough to let you figure out the total picture on your own. If there actually is demand for another sample project, let me know in the comments and I’ll see if I can free up some time to make one.



