Letting your UnitOfWork & Repositories support multiple frameworks
A little while ago I found myself in a situation where I had to replace Microsoft’s Entity Framework with NHibernate. I had figured this wouldn’t be too much of a problem! I had neatly used a Unit of Work and Repository implementation for all my data access needs, so I felt pretty secure in the knowledge that I should be able to switch quite quickly between the two.
Eventually I did, it wasn’t near as quick as I had initially thought though. I discovered that all these articles on the internet describe how to implement a Unit of Work for a single framework! The one I had written was focused solely on the Entity Framework and was pretty much useless for NHibernate. Dismayed, I decided to scrap what I had and start over with the design. This time specifically keeping in mind that I might want to be able to swap between the two at some point in the future.
Let’s start at the begin. To be able to create a common interface for my repositories, and a common way to interact with my repositories, I decided I would need a common UnitOfWork interface as well. It doesn’t have to do anything, as long as it gives me the ability to cast my objects back and forth.
/// <summary>
/// Doesn't do anything besides providing a common interface.
/// </summary>
/// <remarks>
/// TODO: Distinguish common functionality later and implement it!
/// </remarks>
public interface IUnitOfWork : IDisposable
{
}
Well, that was easy! I now have my interface. I can already picture the frown on your face. After all, what’s the bloody point of an interface without methods?! Admittedly, not much at first sight. Bear with me and you’ll find out why I’ve done this.
The following thing I needed was some sort of interface that allows me to specify the type of context for the Unit of Work. Aha! I smell generics!
/// <summary>
/// Provides a generic interface for a Unit of Work.
/// </summary>
public interface IUnitOfWork<TContext> : IUnitOfWork
{
TContext Context;
void SaveChanges<TEntity>(TEntity entity);
void CancelChanges();
}
Let’s not worry about the specifics yet. The only thing I am worried about here is providing a generic Unit of Work. This way I can simply define a Unit of Work and give the type of the context along with it, for example the ObjectContext for EF4 and ISession for NHibernate! Pretty neat We’re not done yet though. Because the Entity Framework and NHibernate are so different from each other, I decided to implement specific interface for each of those as well. I can imagine different methods might be useful when working with one or the other.
/// <summary>
/// Implement Unit of Work specifically for Entity Framework.
/// Details can be implemented later.
/// </summary>
public interface IEFUnitOfWork : IUnitOfWork<ObjectContext>
{
}
/// <summary>
/// Implement Unit of Work specifically for NHibernate.
/// Details can be implemented later.
/// </summary>
public interface INHUnitOfWork: IUnitOfWork<ISession>
{
}
That’s it! I have now defined the interfaces that will help me with building a common repository interface and a common way of using concrete repositories. Let’s go and have a look at the repositories. Here’s a simple interface for the repositories, it only has three methods for now, but of course you can expand on that to your own liking.
public interface IRepository<T>
{
T GetFirst(Expression<Func<T, bool>> where,
Expression<Func<T, object>>[] eagerFetch);
IEnumerable<T> GetMany(Expression<Func<T, bool>> where,
Expression<Func<T, object>>[] eagerFetch);
IEnumerable<T> GetAll(Expression<Func<T, object>>[] eagerFetch);
}
I can now use this interface to implement different base repositories for Entity Framework as well as NHibernate.
I will start by showing the implementation for the Entity Framework. If you pay close attention to it, you will see that the AddIncludesToObjectSet() method uses a generic Include<T> method. This is an extension method on top of the ObjectQuery object. I’ll probably explain this in a follow-up post!
public abstract class Repository<T> : IRepository<T> where T : class
{
private IEFUnitOfWork unitOfWork;
private ObjectContext objectContext;
private ObjectQuery<T> objectSet;
/// <summary>
/// Constructs an EntityFramework repository.
/// </summary>
/// <param name="unitOfWork">Unit of Work for repository to work with.</param>
public Repository(IUnitOfWork unitOfWork)
{
this.unitOfWork = (IEFUnitOfWork)unitOfWork;
this.objectContext = this.unitOfWork.Context;
}
public virtual T GetFirst(Expression<Func<T, bool>> where,
Expression<Func<T, object>>[] eagerFetch)
{
this.objectSet = this.objectContext.CreateObjectSet<T>();
AddIncludesToObjectSet(eagerFetch);
return this.objectSet
.Where(where)
.FirstOrDefault();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where,
Expression<Func<T, object>>[] eagerFetch)
{
this.objectSet = this.objectContext.CreateObjectSet<T>();
AddIncludesToObjectSet(eagerFetch);
return this.objectSet
.Where(where)
.AsEnumerable();
}
public virtual IEnumerable<T> GetAll(Expression<Func<T, object>>[] eagerFetch)
{
this.objectSet = this.objectContext.CreateObjectSet<T>();
AddIncludesToObjectSet(eagerFetch);
return this.objectSet
.AsEnumerable();
}
private void AddIncludesToObjectSet(Expression<Func<T, object>>[] eagerFetch)
{
if (eagerFetch != null)
{
foreach (Expression<Func<T, object>> fetch in eagerFetch)
{
this.objectSet.Include<T>(fetch);
}
}
}
}
For the NHibernate repository I came up with the following code below this paragraph. Keep in mind that I’ve used the alpha version of NHibernate 3.0 and therefor I’m able to use the NHibernate.Linq namespace easily and call the Query<T>() method on the ISession object!
public abstract class Repository<T> : IRepository<T> where T : class
{
private INHUnitOfWork unitOfWork;
private ISession session;
public Repository(IUnitOfWork unitOfWork)
{
this.unitOfWork = (INHUnitOfWork)unitOfWork;
this.session = this.unitOfWork.Context.SessionFactory.GetCurrentSession();
}
public virtual T GetFirst(Expression<Func<T, bool>> where,
Expression<Func<T, object>>[] eagerFetch)
{
IQueryable<T> query = this.session
.Query<T>()
.Where(where);
query = AddFetchMode(query, eagerFetch);
return query.FirstOrDefault();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where,
Expression<Func<T, object>>[] eagerFetch)
{
IQueryable<T> query = this.session
.Query<T>()
.Where(where);
query = AddFetchMode(query, eagerFetch);
return query.AsEnumerable();
}
public virtual IEnumerable<T> GetAll(Expression<Func<T, object>>[] eagerFetch)
{
IQueryable<T> query = this.session.Query<T>();
query = AddFetchMode(query, eagerFetch);
return query.AsEnumerable();
}
private IQueryable<T> AddFetchMode(IQueryable<T> queryable,
Expression<Func<T, object>>[] eagerFetch)
{
if (eagerFetch != null)
{
foreach (Expression<Func<T, object>> fetch in eagerFetch)
{
queryable = queryable.Fetch(fetch);
}
}
return queryable;
}
}
What have I achieved after all this trouble? That I can write our repositories once and never change them, regardless if I’m working with NHibernate or Entity Framework! Also, this is the part where my useless interface without methods comes into play! I know I have an IUnitOfWork object and I can simply pass it along to the constructor of this repository, regardless of the fact if it is an INHUnitOfWork or IEFUnitOfWork. Perhaps this part can still use a bit of work with type safety, so that there are no accidents with passing along a IEFUnitOfWork to a repository that is expecting a INHUnitOfWork!
public class ConcreteRepository : Repository<TEntity>, IConcreteRepository
{
public ConcreteRepository(IUnitOfWork unitOfWork)
: base(unitOfWork)
{
}
}
The only thing I would need to do when I switch between NHibernate or Entity Framework now is to change the using statement and the project references to point to the proper assembly containing the specific classes and interfaces!
using Storminajar.Framework.Data.NHibernate;
or
using Storminajar.Framework.Data.EntityFramework;
Of course I would still need to make my application aware of which Unit of Work to use and I would have to implement that logic somewhere, but at least I won’t have to change my code of my repositories!


