NHibernate - использовать Session.Flush () в методе Dispose () - хорошая идея или нет? - PullRequest
2 голосов
/ 11 марта 2011

Я использую NHibernate в своем приложении через набор интерфейсов.ISession обернут в интерфейс под названием IUnitOfWork, конкретная реализация которого выглядит следующим образом:

public class UnitOfWork : IUnitOfWork
{
  private ISession _session;

  public UnitOfWork()
  {
    _session = KctcSessionFactory.OpenSession();
  }

  public void Dispose()
  {
    try
    {
      _session.BeginTransaction();
      _session.Flush();
      _session.Transaction.Commit();
    }
    catch (Exception)
    {
      _session.Transaction.Rollback();
      _session.Transaction.Dispose();
      throw;
    }
  }

  public T Load<T>(int id)
  {
    return _session.Load<T>(id);
  }

  public IQueryable<T> GetList<T>()
  {
    return _session.Linq<T>();
  }

  public void Save(object entity)
  {
    _session.SaveOrUpdate(entity);
  }

  public void Delete(object entity)
  {
    _session.Delete(entity);
  }
}

IUnitOfWork обрабатывается замком Виндзор и имеет стиль жизни PerWebRequest.Это означает, что IUnitOfWork фактически является единичным с точки зрения веб-запроса и автоматически удаляется в конце запроса.

Этот код выглядит для меня абсолютно надежным.Любые сохраненные сущности будут сброшены, когда IUnitOfWork будет расположен в конце веб-запроса, и вся операция будет заключена в транзакцию, поэтому она выполняется атомарно.Мне никогда не нужно использовать транзакции вне этого класса.Я прав?Этот код безопасен?Или я пропустил что-то ужасное?

Отредактировано для ясности: Я хочу знать, правильно ли я предполагаю, что вся очистка / фиксация в базе данных будет ждать, пока сеанс не будет ликвидирован, ипоэтому может быть заключен в одну транзакцию в методе Dispose.Или есть ситуации, когда данные могут быть сброшены, прежде чем я сделаю это явно в методе Dispose?Или где мне может понадобиться явно использовать транзакции по какой-то другой причине?

Ответы [ 2 ]

2 голосов
/ 11 марта 2011

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

1 голос
/ 13 марта 2011

Я вижу несколько проблем с тем, как вы используете NHibernate.

1) Если вы используете NHibernate с настройками по умолчанию, он работает в режиме сброса AUTO, что означает, что изменения иногда сбрасываются в БД при выполнении запроса, чтобы избежать устаревших результатов. Это может означать, что данные будут преждевременно очищены вне вашей транзакции.

2) Когда ваша транзакция охватывает только операцию сброса, чтения происходят вне транзакции и не подлежат тому же уровню изоляции, что и транзакция. Чтобы транзакция была по-настоящему атомарной и изолированной, вам нужно начать транзакцию сразу после создания сеанса.

Лучше было бы что-то вроде этого:

public class UnitOfWork : IDisposable
{
    ISession currentSession;
    bool shouldCommit;

    public UnitOfWork()
    {
        currentSession = KctcSessionFactory.OpenSession();
        currentSession.BeginTransaction();
    }

    public void Commit()
    {
        shouldCommit = true;
    }

    public void Dispose()
    {
        if (shouldCommit)
        {
            currentSession.Transaction.Commit();
        }
        else
        {
            currentSession.Transaction.Rollback();
        }

        currentSession.Dispose();
    }
}

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

Имеет ли это смысл?

...