In this recipe, we'll show you how to set up the data access layer to wrap all data access in NHibernate transactions automatically.
Complete the Eg.Core
model and mappings from Chapter 2, Models and Mappings.
Eg.Core.Data
.Eg.Core.Data
using the NuGet Package Manager Console.DataAccessObject
classes:public class DataAccessObject<T, TId> where T : Entity<TId> { private readonly ISessionFactory _sessionFactory; private ISession session { get { return _sessionFactory.GetCurrentSession(); } } public DataAccessObject(ISessionFactory sessionFactory) { _sessionFactory = sessionFactory; } public T Get(TId id) { return WithinTransaction(() => session.Get<T>(id)); } public T Load(TId id) { return WithinTransaction(() => session.Load<T>(id)); } public void Save(T entity) { WithinTransaction(() => session.SaveOrUpdate(entity)); } public void Delete(T entity) { WithinTransaction(() => session.Delete(entity)); } private TResult WithinTransaction<TResult>(Func<TResult> func) { if (!session.Transaction.IsActive) { // Wrap in transaction TResult result; using (var tx = session.BeginTransaction()) { result = func.Invoke(); tx.Commit(); } return result; } // Don't wrap; return func.Invoke(); } private void WithinTransaction(Action action) { WithinTransaction<bool>(() => { action.Invoke(); return false; }); } } public class DataAccessObject<T> : DataAccessObject<T, Guid> where T : Entity { }
NHibernate requires every data access to occur inside a NHibernate transaction. As we saw with the Transaction action filter recipe in Chapter 3, Sessions and Transactions, this can, in some environments, be accomplished with non-intrusive code elements, such as .NET attributes. This technique is sometimes called Aspect Oriented Programming (AOP).
This recipe, however, shows a more explicit approach. To ensure that at least all our data access layers are wrapped in transactions, we create a private WithinTransaction
method that accepts a delegate and consists of some data access methods, such as session.Save
or session.Get
. The WithinTransaction
method first checks whether the session has an active transaction. If it does, the delegate is invoked immediately. If it does not, a new NHibernate transaction is created, the delegate is invoked and finally the transaction is carried out. If the data access method throws an exception, the transaction will be rolled back automatically, as the exception bubbles up to the using
block.
This transactional auto-wrapping can also be set using SessionWrapper
from the unofficial NHibernate AddIns project at https://bitbucket.org/fabiomaulo/unhaddins. This class wraps a standard NHibernate session. By default, it will throw an exception when the session is used without an NHibernate transaction. However, it can be configured to check for and create a transaction automatically, much in the same way as I have shown you here.
3.145.45.5