I want to move my Unit of work away from my business logic.
In Infrastructure.Data
I have
NHibernateHelper
public class NHibernateHelper
{
private ISessionFactory _sessionFactory;
private readonly string _connectionString;
public NHibernateHelper (string connectionString)
{
if (string.IsNullOrEmpty (connectionString))
throw new HibernateConfigException ("ConnectionString in Web.config is not set.");
_connectionString = connectionString;
}
public ISessionFactory SessionFactory {
get {
return _sessionFactory ?? (_sessionFactory = InitializeSessionFactory ());
}
}
private ISessionFactory InitializeSessionFactory ()
{
return Fluently.Configure ()
.Database (PostgreSQLConfiguration.Standard.ConnectionString (_connectionString).
Dialect ("NHibernate.Dialect.PostgreSQL82Dialect"))
// Use class mappings
.Mappings (m => m.FluentMappings.AddFromAssembly (Assembly.GetExecutingAssembly ()))
// Will Update and create tables if does not exist
.ExposeConfiguration (cfg => new SchemaUpdate (cfg).Execute (true, true))
.BuildSessionFactory ();
}
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
private readonly ITransaction _transaction;
public ISession Session { get; private set; }
public UnitOfWork (ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
Session = _sessionFactory.OpenSession ();
Session.FlushMode = FlushMode.Auto;
_transaction = Session.BeginTransaction (IsolationLevel.ReadCommitted);
}
public void Commit ()
{
if (!_transaction.IsActive) {
throw new InvalidOperationException ("Oops! We don't have an active transaction");
}
_transaction.Commit ();
}
public void Rollback ()
{
if (_transaction.IsActive) {
_transaction.Rollback ();
}
}
public void Dispose ()
{
if (Session.IsOpen) {
Session.Close ();
Session = null;
}
}
}
Repository
public class Repository<TEntity> : IReadWriteRepository<TEntity>
where TEntity : class
{
private readonly ISession _session;
public Repository (ISession session)
{
_session = session;
}
#region IWriteRepository
public bool Add (TEntity entity)
{
_session.Save (entity);
return true;
}
public bool Add (System.Collections.Generic.IEnumerable<TEntity> entities)
{
foreach (TEntity entity in entities) {
_session.Save (entity);
}
return true;
}
public bool Update (TEntity entity)
{
_session.Update (entity);
return true;
}
public bool Update (System.Collections.Generic.IEnumerable<TEntity> entities)
{
foreach (TEntity entity in entities) {
_session.Update (entity);
}
return true;
}
public bool Delete (TEntity entity)
{
_session.Delete (entity);
return true;
}
public bool Delete (System.Collections.Generic.IEnumerable<TEntity> entities)
{
foreach (TEntity entity in entities) {
_session.Delete (entity);
}
return true;
}
#endregion
#region IReadRepository
public System.Linq.IQueryable<TEntity> All ()
{
return _session.Query<TEntity> ();
}
public TEntity FindBy (System.Linq.Expressions.Expression<System.Func<TEntity, bool>> expression)
{
return FilterBy (expression).SingleOrDefault ();
}
public TEntity FindBy (object id)
{
return _session.Get<TEntity> (id);
}
public System.Linq.IQueryable<TEntity> FilterBy (System.Linq.Expressions.Expression<System.Func<TEntity, bool>> expression)
{
return All ().Where (expression).AsQueryable ();
}
#endregion
}
In Intrastructure.DependencyInjectrion
I have:
public void RegisterServices (SimpleInjector.Container container)
{
var connectionSettings = ConfigurationManager.ConnectionStrings ["Connection"];
container.RegisterPerWebRequest<ISessionFactory> (() => {
NHibernateHelper objNHibernate = new NHibernateHelper (connectionSettings.ConnectionString);
return objNHibernate.SessionFactory;
});
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork> ();
container.RegisterPerWebRequest<ISession> (() => {
UnitOfWork unitOfWork = (UnitOfWork)container.GetInstance<IUnitOfWork> ();
return unitOfWork.Session;
});
container.RegisterOpenGeneric (typeof(IReadWriteRepository<>), typeof(Repository<>));
}
Then in my service I would do something like this:
Web.UI.Services.CompanyService
public void CreateNewCompany (Company company)
{
if (_companyRepository.Add (company))
_unitOfWork.Commit ();
else
_unitOfWork.Rollback ();
}
Would it be a better practice to call _unitOfWork.Commit()
or _unitOfWork.Rollback()
in the eneric Repository
rather than in the in the Service
layer?
I was thinking of improving the generic Repository
by injecting the IUnitOfWork
into it while adding some additional error handling in too.
If this is not a good approach, can someone give me some direction to improve this? Note: I do want to keep repository pattern
in case we choose to switch ORM
in a few years time.
Managing transactions in the repository is definitely not the standard way to do it, as it removes the possibility to implement business logic that spans multiple repositories (or multiple actions on the same repository) and is required to be executed atomically.
I would try to keep the transaction management on the top-most layer that makes sense, i.e. that does not impair your ability to re-use business logic (including transaction management) if you decided to host your application in a different environment for example. This would seem to be the service layer in your case; you could also distinguish between different types of services as done in Domain-Driven Design where there is a difference between application services and domain services. The application services might orchestrate multiple domain services and handle the management of transactions and potentially the unit of work).
Hope this helps a bit.