I read a lots of content about the usage of Entity Framework/NHibernate (or basically any other modern ORM) with the repository/UnitOfWork patterns. Apparently the community is divided. Some would say the repository pattern is almost mandatory, some others would say that it's a waste of time... Well, I come up with my "own" design and I just wanted to share it with you in order to get some feedback...
In the past, my company decided to develop and use it's own ORM. It's now a total disaster. Performance, stability (and basically everything else) are terrible. We want to switch to another ORM and we want to be keep the ability to switch from an ORM to another. Indeed, we are now using Sharepoint 2010. It means 3.5 and thus NHibernate 3.4 and Entity Framework 4. We plan to migrate to SharePoint 2013 ASAP in order to be able to rely on .net 4.5/EF 6.1/... So we will have to switch to another ORM pretty soon.
To do so, I have developed a set of classes implementing the "IDatabaseContext" interface.
public interface IDatabaseContext : IDisposable
{
IQueryable<TEntity> AsQueryable<TEntity>()
where TEntity : EntityBase;
IList<TEntity> AsList<TEntity>()
where TEntity : EntityBase;
IList<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate)
where TEntity : EntityBase;
long Count<TEntity>()
where TEntity : EntityBase;
void Add<TEntity>(TEntity entity)
where TEntity : EntityBase;
void Delete<TEntity>(TEntity entity)
where TEntity : EntityBase;
void Update<TEntity>(TEntity entity)
where TEntity : EntityBase;
}
For example, for the prototype I have decided to use NHibernate:
public class NHibernateDbContext : IDatabaseContext
{
private ISession _session = null;
public NHibernateDbContext(ISessionFactory factory)
{
if (factory == null)
throw new ArgumentNullException("factory");
_session = factory.OpenSession();
}
public IQueryable<TEntity> AsQueryable<TEntity>()
where TEntity : EntityBase
{
return _session.Query<TEntity>();
}
public IList<TEntity> AsList<TEntity>()
where TEntity : EntityBase
{
return _session.QueryOver<TEntity>()
.List<TEntity>();
}
public IList<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate)
where TEntity : EntityBase
{
...
}
public long Count<TEntity>()
where TEntity : EntityBase
{
return _session.QueryOver<TEntity>()
.RowCountInt64();
}
public void Add<TEntity>(TEntity entity)
where TEntity : EntityBase
{
if (entity == null)
throw new ArgumentNullException("entity");
UseTransaction(() => _session.Save(entity));
}
public void Delete<TEntity>(TEntity entity)
where TEntity : EntityBase
{
...
}
public void Update<TEntity>(TEntity entity)
where TEntity : EntityBase
{
...
}
private void UseTransaction(Action action)
{
using (var transaction = _session.BeginTransaction())
{
try
{
action();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
public void Dispose()
{
if (_session != null)
_session.Dispose();
}
}
Eventually, my service layer (each entity are associated with a service) relies on this interface so I don't introduce a dependency over the ORM technology.
public class CountryService<Country> : IService<Country>
where Country : EntityBase
{
private IDatabaseContext _context;
public GenericService(IDatabaseContext context)
{
if (context == null)
throw new ArgumentNullException("context");
_context = context;
}
public IList<Country> GetAll()
{
return _context.AsList<Country>();
}
public IList<Country> Find(Expression<Func<Country, bool>> predicate)
{
return _context.Find(predicate);
}
...
}
Eventually, to call a method from the service layer, you just need two lines of code:
var service = new CountryService(new NHibernateDbContext(...)));
or
var service = new CountryService(new TestDbContext(...)));
...
I find this architecture quite simple and very convenient to use. I didn't find (yet) any drawback/flaws/errors.
So what do you think? Did I miss something big? Is there something I can improve?
Thank you for all your feedback...
Regards, Sebastien