У вас может быть несколько транзакций внутри одной сессии в nhibernate? И это плохая идея? - PullRequest
6 голосов
/ 21 марта 2012

Я думаю о создании собственной IUnitOfWork реализации для персистентного слоя NHibernate.

Кажется, что правильный способ сделать это - создать экземпляры ISession и ITransactionв конструкторе, а затем располагаются в деструкторе или в методе Dispose().

Конечно, если кто-то вызовет метод Save(), тогда ISession будет сброшен, а ITransaction будетзавершено, поэтому после вызова Save() не будет действительной открытой транзакции для Save() снова ... если я не совершу первую транзакцию и не сразу открою другую, новую транзакцию.Но разве это хорошая идея?

С точки зрения дизайна, имеет смысл выполнить одну операцию фиксации, но я не обязательно буду контролировать код, и другие разработчики могут быть менее дисциплинированными в отношении следования шаблону UnitOfWork.

Потеряю ли я что-либо, пытаясь сделать UnitOfWork толерантным к нескольким транзакциям за сеанс?Стоит ли просто проверять открытую транзакцию и выдавать исключение, если оно уже было зафиксировано, а не совершать новую транзакцию?

Ответы [ 3 ]

11 голосов
/ 21 марта 2012

Чтобы ответить на первый вопрос: да, возможно иметь несколько транзакций за один сеанс.

Это хорошая идея?Это зависит.

Проблема заключается в том, что изменение данных в первой транзакции будет зафиксировано, хотя неясно, будет ли завершена вся единица работы (сеанс) в конце.Когда вы получите, скажем, исключение StaleObjectException в более поздней транзакции, вы уже зафиксировали некоторые данные.Обратите внимание, что такое исключение делает вашу сессию непригодной для использования, и вам все равно пришлось ее уничтожить.Тогда трудно начать все сначала и повторить попытку.

Я бы сказал, это хорошо работает при следующих обстоятельствах:

  • Это приложение пользовательского интерфейса
  • Изменениятолько в последней транзакции.

Приложение пользовательского интерфейса

Ошибки обрабатываются пользователем в интерактивном режиме.Это означает, что пользователь может видеть, что на самом деле хранится в случае ошибки, и повторяет сделанные им изменения.

Изменения сбрасываются только в последней транзакции

Сеанскак реализовано NH только сбрасывает изменения в конце или «когда необходимо».Так что было бы возможно сохранить изменения в памяти, пока сеанс не будет зафиксирован.Проблема в том, что NH должен очищать сессию перед каждым запросом, что сложно контролировать.Его можно отключить, что приводит к побочным эффектам.При написании простых транзакций вы можете контролировать это.В сложной системе практически невозможно убедиться, что все в порядке.

The Simple Way (tm)

Я написал слой постоянства довольно крупного клиентасистемаВ такой системе пользователь не обрабатывает ошибки напрямую.Вам необходимо обработать ошибки в системе и вернуть управление клиенту в согласованном состоянии.

Я упростил всю обработку транзакций до абсолютного минимума, чтобы сделать ее стабильной и «защищенной от идиотов».У меня всегда есть сеанс и транзакция, созданная вместе, и она либо фиксируется, либо нет.

2 голосов
/ 21 марта 2012

Существует несколько вариантов реализации вложенной транзакции nhibernate с единицей работы.

Здесь я использую шаблон команды для единицы работы.

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();
    }
}
0 голосов
/ 21 марта 2012

Я думаю, что решение с одной транзакцией на единицу работы является слишком ограничительным.В некоторых средах может потребоваться возможность выполнять несколько транзакций за сеанс.Я сам управляю транзакциями явно, и это кажется гибким решением.

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

Использование выглядит следующим образом.Легко встраивается в любой шаблон.

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