In my previous post Conversation Per Business Transaction using PostSharp and IoC I dealt with the session management aspect of NHiberate however by the end of my development of the business conversation aspect I realized that my current data provider scheme created a lot of duplicate code. As we all know any code that is duplicated becomes a nightmare to maintain so I went through and refactored first a truly generic repository.
public interface IRepository<T>
{
ICriteria GetAll();
T Get<U>(U id);
void Save(T obj);
ICriteria GetRange(int resultSet, int rangeSize);
}
|
This is a rather basic interface that will handle the most common operations to my data providers, currently it can handle getting all of the items in my database for a type, a page collection into the list of items in my database, get a specific item and save a new item (or update an existing item). The only minorly unsightly part is the Get<U> method this just allows passing any type of ID instead of declaring it as int statically.
On to implementing the interface:
public class Repository<T> : IRepository<T> { private readonly IConversation _conversation; public Repository(IConversation conversation) { _conversation = conversation; } #region IRepository<T> Members ICriteria IRepository<T>.GetAll() { return _conversation.Session.CreateCriteria(typeof (T)) //.SetCacheable(true) //.SetCacheMode(CacheMode.Normal) ; } T IRepository<T>.Get<U>(U id) { return _conversation.Session.Get<T>(id); } void IRepository<T>.Save(T obj) { _conversation.Session.SaveOrUpdate(obj); } ICriteria IRepository<T>.GetRange(int resultSet, int rangeSize) { return _conversation.Session.CreateCriteria(typeof (T)) //.SetCacheable(true) //.SetCacheMode(CacheMode.Normal) .SetFirstResult(resultSet*rangeSize) .SetMaxResults(rangeSize - 1) ; } #endregion } |
This class contains rather basic interactions with NHiberate, if you’re not familiar with the CreateCriteria querying method in NHibernate take a look at Chapter 12. Criteria Queries. Other than that I can at an application level enable or disable output caching for my generic repository, currently I’m still working on getting my second level caching implementation working so right now I have it disabled as you can see it’s commented out.
By returning the ICriteria from the repository it allows us to pass on the full power of NHibernate and allow us to create further data provider specific queries.
Next I created an abstract base class for my actual data providers:
public abstract class DataProviderBase<T>
{
protected readonly IConversation _conversation;
protected readonly IRepository<T> _repoistory;
protected DataProviderBase(IConversation conversation)
{
_conversation = conversation;
_repository = new Repository<T>(_conversation);
}
}
|
This base class just holds the fields for the business conversation (or ISession) and my newly created generic repository. Now on to my true concrete implementation of my data provider:
public class EmployeeDataProvider : DataProviderBase<Employee>, IEmployeeDataProvider
{
[InjectionConstructor]
public EmployeeDataProvider(IConversation conversation) : base(conversation)
{
}
#region IEmployeeDataProvider Members
IList<Employee> IEmployeeDataProvider.GetAll()
{
return _repository.GetAll().List<Employee>();
}
IList<Employee> IEmployeeDataProvider.GetRange(int resultSet, int rangeSize)
{
return _repository.GetRange(resultSet, rangeSize).List<Employee>();
}
Employee IEmployeeDataProvider.Get(int employeeId)
{
return _repository.Get(employeeId);
}
void IEmployeeDataProvider.Save(Employee employee)
{
_repository.Save(employee);
}
IList<Territory> IEmployeeDataProvider.GetTerritories()
{
return _conversation.Session.CreateCriteria(typeof (Territory)).List<Territory>();
}
#endregion
}
|
In this class we have the delegation of the IRepository (sitting in the abstract base) and we also have direct access to the NHibernate Session so we can add further methods of any kind. This also allows us the flexibility if you’d rather the EmployeeDataProvider had GetEmpoyees() instead of GetAll() it would allow that easily.
Since the repository itself is not exposed at the provider level this allows us to return the actual results in anyway we want. It allows us to further modify the basic queries by adding NHibernate where clauses to the Criteria if we so choose. Since the actual ICriteria won’t be exposed outside of the data provider we don’t need to worry about client interactions causing database usage. This would also allow us if we added NHibernate LINQ to not be concerned with returning the Queryable results for the same reason.
This pattern would also work directly with LINQ2SQL / LINQ2Entities / DLINQ by changing where the NHibernate session is dealt with and using your entity provider. I feel this is a perfect to the data access patern as it protects the code from becoming too unweilding by basing all communication directly through the ICriteria and giving our data providers concrete result lists.
Let me know what you think!
BloggingContext.ApplicationInstance.CompleteRequest();