开发者

NHibernate - use Session.Flush() in Dispose() method - good idea or not?

开发者 https://www.devze.com 2023-02-16 19:07 出处:网络
I use NHibernate in my application through a set of interfaces. The ISession is wrapped up in an interface called IUnitOfWork whose concrete implementation looks like this:

I use NHibernate in my application through a set of interfaces. The ISession is wrapped up in an interface called IUnitOfWork whose concrete implementation looks like this:

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.T开发者_如何学运维ransaction.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);
  }
}

The IUnitOfWork is handled by castle windsor, and has a lifestyle of PerWebRequest. This means that the IUnitOfWork is effectively a singleton from the point of view of a web request, and is disposed automatically at the end of the request.

This code looks totally foolproof to me. Any saved entities will be flushed when the IUnitOfWork is disposed at the end of the web request, and the whole operation is wrapped in a transaction so it's done atomically. I shouldn't ever need to use transactions outside this class. Am I right? Is this code safe? Or have I missed something horrible?

Edited for clarity: I want to know if I am correct in assuming that all flushing/committing to the database will wait until the session is disposed, and therefore can be wrapped in a single transaction in the Dispose method. Or are there situations where data may be flushed before I do it explicitly in the Dispose method? Or where I might need to explicitly use transactions for some other reason?


Committing the transaction will automatically flush the session, so no you don't need to. I wouldn't put Load and GetList methods in a unit of work, since that's more of a concern for a Repository or DAO. Otherwise it looks fine to me.


I see several problems with the way you are using NHibernate.

1) If you are using NHibernate with default settings, it is running with flush mode AUTO, which means that changes will sometimes be flushed to the db when you execute a query in order to avoid stale results. This could mean that stuff would be flushed prematurely, outside of your transaction.

2) When your transaction only spans the flush operation, reads occur outside of the transaction and are not subject to the same isolation level as the transaction. For the transaction to be truly atomic and isolated, you need to start a transaction immediately after creating the session.

A better uow would be something like this:

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

allowing usage scenarios where someone would need to somehow check if the web request succeeded with no exceptions, calling Commit on the current uow if that was the case.

Does that make sense?

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号