Can you make multiple transactions within the same session in nhibernate? And is that a bad idea?

I am thinking of creating my own implementation of IUnitOfWork for the NHibernate persistence layer.

It seems that the right way to do this would be to create an ISession and ITransaction created in the constructor and then located in the destructor or Dispose() method.

Of course, if someone calls the Save() method, then ISession will be cleared, and ITransaction will be completed, so after calling Save() there will not be a valid open transaction for Save() again ... if I did not complete the first transaction and immediately opened another, new transaction. But is that a good idea?

Structurally, it makes sense to do one commit operation, but I will not necessarily control the code, and other developers may be less disciplined with respect to the UnitOfWork template.

Can I lose / gain anything trying to make UnitOfWork tolerance with multiple transactions per session? Should I just check for an open transaction and throw an exception if it has already been committed, instead of making a new transaction?

+6
source share
3 answers

To answer the first question: yes, it is possible to have several transactions in one session.

Is that a good idea? It depends.

The problem is that the data change in the first transaction will be performed, while it is not sure that the whole unit of work (session) will be fixed at the end. When you get, say, a StaleObjectException in a later transaction, you already have certain data. Note that this type of exception makes your session unusable, and you still had to destroy it. Then it’s hard to start all over and try again.

I would say it works well in these conditions:

  • This is a user interface application.
  • The changes only turned red in the last transaction.

User interface application

Errors are handled by the user interactively. This means that the user can see what is actually stored in case of an error and repeats the changes made.

Changes are only painted in the last transaction

A NH session only discards changes at the end, or "if necessary." In this way, you can save the changes to memory until the session is committed. The problem is that NH needs to clear the session before every request that is difficult to control. It can be turned off, which leads to side effects. When writing simple transactions, you can manage it. In a complex system, it is almost impossible to verify that nothing is happening.

Easy way (tm)

I wrote the persistence level of a rather large client-server system. In such a system, you do not have user processing errors directly. You need to handle errors in the system and return control to the client in a consistent state.

I have simplified all transaction processing to an absolute minimum to make it stable and “idiotic proof”. I always have a session and a transaction created together, and it becomes either perfect or not.

+11
source

There are several options for implementing nhibernate nested transactions with a unit of work.

Here I use the command template for the unit of work.

 public interface IAction { void Execute(); } public abstract class Action<T> : IAction, IDisposable where T : Action<T> { public void Execute() { try { //Start unit of work by your unit of work pattern or transaction.Begin(); OnExecute(); //End Unit of work transaction.Commit(); } catch (Exception e) { transaction.Rollback(); throw e; } } protected abstract void OnExecute(); public void Dispose() { } } public class MyBusinessLogic : Action<MyBusinessLogic> { protected override void OnExecute() { //Implementation } } public class MyAnotherBusinessLogic : Action<MyAnotherBusinessLogic> { protected override void OnExecute() { //Nested transaction MyBusinessLogic logic = new MyBusinessLogic(); logic.Execute(); } } 
+2
source

I think the solution with one transaction per unit of work is too restrictive. In some environments, you may need to be able to perform multiple transactions per session. I manage the transactions myself explicitly and seem to be a flexible solution.

 public interface IUnitOfWork: IDisposable { IGenericTransaction BeginTransaction(); } public interface IGenericTransaction: IDisposable { void Commit(); void Rollback(); } public class NhUnitOfWork: IUnitOfWork { private readonly ISession _session; public ISession Session { get { return _session; } } public NhUnitOfWork(ISession session) { _session = session; } public IGenericTransaction BeginTransaction() { return new NhTransaction(_session.BeginTransaction()); } public void Dispose() { _session.Dispose(); } } public class NhTransaction: IGenericTransaction { private readonly ITransaction _transaction; public NhTransaction(ITransaction transaction) { _transaction = transaction; } public void Commit() { _transaction.Commit(); } public void Rollback() { _transaction.Rollback(); } public void Dispose() { _transaction.Dispose(); } } 

Usage is as follows. It is easily included in any template.

 public void Do(IUnitOfWork uow) { using (var tx = uow.BeginTransaction()) { // DAL calls tx.Commit(); } } 
0
source

Source: https://habr.com/ru/post/911255/


All Articles