Another NHibernate Repository
I’ve been having a lot of fun with NHibernate recently and, during my spare time, have a few pet projects that I like to work on to improve my skillset (especially because it looks like my company is going to give an ORM a test run in the not too distant future). Everyone has their opinion on whether or not a Repository with an ORM is a good thing or a bad thing, and I’m not going to rehash any such arguments, except to say the following:
- I needed to stop implementing the same old LoadSomethingById, UpdateSomething, SaveSomething, etc. methods for every single data type in my service layer. I found myself re-inventing the wheel, albeit with different naming conventions, over and over again
- While NHibernate does provide a layer of abstraction in and of itself, I was reluctant to couple a particular ORM to my solution (i.e. call _session.Load<SomeType>(id) directly from my controller/codebehind/whatever)
My implementation is similar to a number already out there, aiming for a minimal set of non-specialized operations in my repository contract. Included is a pretty
My initial feeling is that this implementation as-is takes care of the bulk (~70%) ordinary queries. For extraordinary queries, and those used repeatedly that I would like to reuse, I use extension methods rather than a service layer (as in the example below). This provides specific functions to specific repositories (based on the generic type) and allows me to use a single
All in all, it looks a little something like this:
Here is the repository interface:
public interface IRepository<T, TId>
{T Get(TId id);
IList<T> GetAll();T SaveOrUpdate(T entity);void Delete(T entity);ISession Session { get; }IList<T> FindMany(Expression<Func<T, bool>> expression = null, int skip = 0, int take = int.MaxValue);IEnumerable<T> FindManyFuture(Expression<Func<T, bool>> expression = null, int skip = 0, int take = int.MaxValue);T FindOne(Expression<Func<T, bool>> expression);T Load(TId id);T Save(T entity);T Update(T entity);void Delete(TId id);}
Here is the default implementation:
public class NHibernateRepository<T, TId> : IRepository<T, TId> where T : class{private readonly ISession _session;
public NHibernateRepository(ISession session){_session = session;
}public virtual ISession Session{get { return _session; }
}public IList<T> FindMany(Expression<Func<T, bool>> expression = null, int skip = 0, int take = int.MaxValue ){return expression != null ? Session.QueryOver<T>().Where(expression).Skip(skip).Take(take).List() : Session.QueryOver<T>().Skip(skip).Take(take).List();}public IEnumerable<T> FindManyFuture(Expression<Func<T, bool>> expression = null, int skip = 0, int take = int.MaxValue){return expression != null ? Session.QueryOver<T>().Where(expression).Skip(skip).Take(take).Future<T>() : Session.QueryOver<T>().Skip(skip).Take(take).Future<T>();}public T FindOne(Expression<Func<T, bool>> expression)
{return Session.QueryOver<T>().Where(expression).SingleOrDefault();
}public virtual T Load(TId id)
{return Session.Load<T>(id);
}public virtual T Save(T entity)
{Session.Save(entity);
return entity;}public virtual T Update(T entity)
{Session.Update(entity);
return entity;}public virtual void Evict(T entity)
{Session.Evict(entity);
}public virtual void Delete(T entity)
{Session.Delete(entity);
}public virtual void Delete(TId id)
{Session.Delete(Session.Load<T>(id));}public virtual T Get(TId id){return Session.Get<T>(id);}public virtual IList<T> GetAll()
{ICriteria criteria = Session.CreateCriteria(typeof (T));
return criteria.List<T>();}public virtual T SaveOrUpdate(T entity)
{Session.SaveOrUpdate(entity);
return entity;}}
Example Extension Method
public static class ShopperExtensions{public static Shopper GetShopperBySecurityToken(this IRepository<Shopper,int> repository, string securityToken){return repository.Session.CreateCriteria(typeof (Shopper)).CreateAlias("ShopperSecurityProfile", "profile").Add(Property.ForName("profile.SecurityToken").Eq(securityToken)).UniqueResult<Shopper>();}}
Basic Usage
var shopperRepository = ObjectFactory.Container.GetInstance<IRepository<Shopper,int>>();
int shopperID = 123456;
var shopper = shopperRepository.Get(123456);// OR USING SECURITY TOKEN AND EXTENSION METHOD
var shopper = shopperRepository.GetShopperBySecurityToken(securityToken);
In the above code you can see an example of using the Get method to get a particular shopper based on their ID and also a second example where an extension method is used to return a shopper based on their security token. (Note, in real-world code I would probably use constructor/property injection for my dependencies but go directly to the container in this example (ObjectFactory.Container…) for the sake of brevity.) The one change I definitely need to make is to remove the dependency on ISession from the repository interface. This (and now that I think of it maybe the Evict method) is the only place in the solution where the specific ORM solution leaks into my solution (so to speak). This property is only necessary so that any extension methods defined have a session they can use to hit the database.
Comments