Searching on StackOverflow and google i find many and many different implementation but none of that really convinced me, each had some small defect that prevented me from using it. So I tried to create my own version and would like to know what you think.
Essentially everything is based on the use of interfaces and UnitOfWork pattern built in the style of the Entity Framework, even if I use NHibernate as implementation.
Wanting to have absolutely generic interfaces and can therefore accommodate both EF NH my UoW does not expose any of the specific concepts of the two repositories such as session or transaction
this is the usage:
using (IUnitOfWork uow = new NHUnitOfWork())
{
uow.StartOperation();
Class1 c1 = _uow.Class1Repo.Read<Class1>(idClasse1);
c1.Desc = "edited";
uow.Class1Repo.Update(c1);
}
of course the new instance can be avoided using dependency injection.
As you can see the only dependency is to Unit of Work, not to every repository, with advantage in terms of maintainability and scalability
first i create an interface for my entities which define the key
>public interface IKeyedEntity<TKeyType> where TKeyType : struct
{
TKeyType Id { get; set; }
}
these are the interfaces:
public interface IUnitOfWork : IDisposable{
void StartOperation();
void SaveAll();
void DiscardChanges();
void OpenConnection();
void CloseConenction();
IClass1Repository Class1Repo { get; }
}
At this point I created the basic interface of my generic repository containing all the operations that must be provided (if you want you can create generic repository for distinct divide the reading operations from those of writing so that you can manage the repository with limited permissions for objects that not foresee the change, but I had no need of this type)
public interface IRepoBase<TEntity, TKey> : IDisposable
where TEntity : class, IKeyedEntity<TKey>
where TKey : struct {
void Create(TEntity obj);
T Read<T>(TKey id);
void Update(TEntity obj);
void Delete(TEntity obj);
}
all the remaining read operations of individual objects will be managed in their specific repository
public interface IClass1Repository : IDisposable, IRepoBase<Class1, int>
{
IEnumerable<Class1> FindAll();
string GetFirstRecordDescription();
}
all the conditions and filters to queries will be made directly into the repository using more specific methods, why not use it IEnumerable IQueryable. In this way they are not even linked to Linq
public class NHUnitOfWork: IUnitOfWork{
private ITransaction _transaction;
private NHSessionFactory _nhHelper;
private ISession _session;
public IClass1Repository Class1Repo { get; private set; }
public NHUnitOfWork(NHSessionFactory nhHelper) {
_nhHelper = nhHelper ?? new NHSessionFactory();
InitializeSession(); }
public NHUnitOfWork() {
_nhHelper = new NHSessionFactory();
InitializeSession(); }
private void InitializeSession() {
if (_session == null)
_session = _nhHelper.OpenSession();
Class1Repo = new Class1Repository(this._session);
public void StartOperation() {
InitializeSession();
_transaction = _session.BeginTransaction(); }
public void SaveAll() {
if (_transaction != null) {
_transaction.Commit();
_transaction = null; } }
public void DiscardChanges() {
Contract.Ensures(_session.Transaction.IsActive == false);
if (_transaction != null) {
_transaction.Rollback();
_transaction = null; } }
public void OpenConnection() {
InitializeSession(); }
public void CloseConnection() {
if (_transaction != null) {
_transaction.Dispose();
_transaction = null; }
if (_session != null) {
_session.Dispose();
_session = null; }
}
}
OpenConnection and CloseConenction hide the nhibernate session, while StartOperation, SaveAll and DiscardChanges hide the concept of atomic operation (transaction)
NH implementation of BaseRepository:
public class NHRepoBase<TEntity, TKey> : IRepoBase<TEntity, TKey>
where TEntity : class, IKeyedEntity<TKey>
where TKey : struct
{
protected ISession _session;
public NHRepoBase(ISession session)
{_session = session; }
public void Create(TEntity obj)
{ _session.SaveOrUpdate(obj); }
public void Update(TEntity obj)
{ _session.Merge<T>(obj); }
public void Delete(TEntity obj)
{ _session.Delete(obj); }
public T Read<T>(TKey id)
{ return _session.Get<T>(id); }
private bool disposed = false;
protected virtual void Dispose(bool disposing) {
if (!this.disposed){
if (disposing)
_session.Dispose();
}
this.disposed = true;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
A simple specific repository:
class Class1Repository : NHRepoBase<Class1, int>, IClass1Repository, IDisposable {
public Class1Repository(ISession session) : base(session) { }
public IEnumerable<Class1> FindAll()
{ return _session.QueryOver<Class1>().List(); }
public string GetFirstRecordDescription()
{ return _session.QueryOver<Class1>().Select(c => c.Desc).SingleOrDefault<string>(); }
}
Thanks in advance for any suggestions or feedback on my implementation