Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
383 views
in Technique[技术] by (71.8m points)

session - Correct use of the NHibernate Unit Of Work pattern and Ninject

I have the following implementation and would like some feedback as to whether it makes correct use of NHibernate for sessions and transactions.

public interface IUnitOfWork : IDisposable
{
    ISession CurrentSession { get; }
    void Commit();
    void Rollback();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory _sessionFactory;
    private readonly ITransaction _transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
        CurrentSession = _sessionFactory.OpenSession();
        _transaction = CurrentSession.BeginTransaction();
    }

    public ISession CurrentSession { get; private set; }

    public void Dispose()
    {
        CurrentSession.Close();
        CurrentSession = null;
    }

    public void Commit()
    {
        _transaction.Commit();
    }

    public void Rollback()
    {
        if (_transaction.IsActive) _transaction.Rollback();
    }
}

Ninject binding

Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
Bind<ISessionFactory>().ToProvider<NHibernateSessionFactoryProvider>().InSingletonScope();
Bind<IRepository>().To<Repository>().InTransientScope();

Here is an example of the usage:

public class Repository : IRepository
{
    private readonly ISessionFactory _sessionFactory;

    public Repository(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public void Add(IObj obj)
    {
        using (var unitOfWork = new UnitOfWork(_sessionFactory))
        {
            unitOfWork.CurrentSession.Save(obj);
            unitOfWork.Commit();
        }         
    }
}

In my previous implementation I would inject IUnitOfWork into my repository constructor like so

public Repository(IUnitOfWork unitOfWork)
    {...

But the Dispose() method would not execute causing a subsequent call to throw this exception: "Cannot access a disposed object. Object name: 'AdoTransaction'."

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First observation: your repository should not commit the unit of work. This defeats the whole point of the unit of work pattern. By immediately saving your changes inside the repository, you're "micro managing" the NHibernate Session.

The unit of work should be referenced higher up the stack, in your application/service layer. This allows you to have application code that performs several actions, potentially on different repositories, and still at the end commit everything at once.

The UnitOfWork class itself looks Ok, though you should ask yourself if you really need it. In NHibernate, the ISession IS your unit of work. Your UnitOfWork class does not seem to add a lot of value (especially since you're exposing the CurrentSession property anyway)

But you do need to think about it's lifetime. I think you have it wrong on this point. Session lifetime management depends on the type of application you're developing: in a web app, you typically want to have a unit of work per request (you might want to google on 'nhibernate session per request'). In a desktop app it's slightly more complicated, you will most of the time want a 'session per screen' or 'conversation per business transaction'.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...