Click here to Skip to main content
15,867,704 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
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:
C#
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

C#
>public interface IKeyedEntity<TKeyType> where TKeyType : struct
    {
        TKeyType Id { get; set; }
    }



these are the interfaces:

C#
public interface IUnitOfWork : IDisposable{
        void StartOperation();
        void SaveAll();
        void DiscardChanges();
        void OpenConnection();
        void CloseConenction();

        IClass1Repository Class1Repo { get; }
                // All other repository here
    }


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)


C#
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

C#
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

C#
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);
                        // All other repository here    }

        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;    }
        }

        // Disposable pattern here
    }


OpenConnection and CloseConenction hide the nhibernate session, while StartOperation, SaveAll and DiscardChanges hide the concept of atomic operation (transaction)

NH implementation of BaseRepository:

C#
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:

C#
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
Posted
Updated 10-Jun-14 23:09pm
v4
Comments
Duncan Edwards Jones 10-Jun-14 7:01am    
GetDescription() only ever returns the description of the first item in the repository - is this what you intend?
gt.guybrush 10-Jun-14 8:14am    
yes, it was only an example of some specific method, i edit the method name to explicit it
Duncan Edwards Jones 10-Jun-14 8:47am    
In that case it is looking good. I tend to specify the type of the record key as well as the record type rather than passing an object (e.g. http://www.codeproject.com/Reference/731015/The-repository-pattern-in-VB-Net ) .. but this is a personal style thing.
gt.guybrush 11-Jun-14 3:44am    
The key entity is interesting, in this way i can use typed key and not a simply object, but c# does not support member aliasing so i cant find a simply way to achieve it
what I do not like very much is having to introduce this concept in my items, I do not know if it fits with the DDD logic
gt.guybrush 11-Jun-14 5:09am    
find the way and post updated

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900