How to wrap lazy loading in a transaction?
Asked Answered
H

1

1

I am using nhibernate and the nhibernate profile what keeps throwing this alert.

Use of implicit transactions is discouraged"

I actually wrap everything in a transaction through ninject

  public class NhibernateModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
            Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope()
                                                                                      .OnActivation(StartTransaction)
                                                                                      .OnDeactivation(CommitTransaction);
        }

        public void CommitTransaction(ISession session)
        {

            if (session.Transaction.IsActive)
            {
                session.Transaction.Commit();
            }

        }

        public void StartTransaction(ISession session)
        {
            if (!session.Transaction.IsActive)
            {
                session.BeginTransaction();
            }
        }
    }

So this should wrap everything in a transaction and it seems to work with anything that is not lazy loading.

If it is lazy loading though I get the error. What am I doing wrong.

Harar answered 5/6, 2011 at 20:37 Comment(4)
When you say "the error", you mean "implicit transactions is discouraged"?Amagasaki
@Merlyn Morgan-Graham - Yes that is the error I am getting because for some reason it is not wrapping my statements in a transaction when I call it through lazy loading.Harar
This blog post (comments section) seems to tell me that transactions don't make sense with lazy loading - ayende.com/blog/3775/…Amagasaki
@ Merlyn Morgan-Graham - I just got from the one comment in that post about how you should manage your load better.Harar
R
1

This is, in fact, still an implicit transaction, or relatively close to it. The injector is blissfully ignorant of everything that's happened between activation and deactivation and will happily try to commit all your changes even if the state is incorrect or corrupted.

What I see is that you're essentially trying to cheat and just have Ninject automatically start a transaction at the beginning of every request, and commit the transaction at the end of every request, hoping that it will stop NH from complaining. This is extremely bad design for several reasons:

  1. You are forcing a transaction even if the session is not used at all (i.e. opening spurious connections).
  2. There is no exception handling - if an operation fails or is rolled back, the cleanup code simply ignores that and tries to commit anyway.
  3. This will wreak havoc if you ever try to use a TransactionScope, because the scope will be completed before the NH transaction is.
  4. You lose all control over when the transactions actually happen, and give up your ability to (for example) have multiple transactions within a single request.

The NH Profiler is exactly right. This isn't appropriate use of NH transactions. In fact, if you're lazy loading, the transaction might end up being committed while you're still iterating the results - not a good situation to be in.

If you want a useful abstraction over the transactional logic and don't want to have to twiddle with ISession objects then use the Unit Of Work pattern - that's what it's designed for.

Otherwise, please code your transactions correctly, with a using clause around the operations that actually represent transactions. Yes, it's extra work, but you can't cheat your way out of it so easily.

Remiss answered 14/8, 2011 at 23:31 Comment(6)
How does the Unit of Work pattern work with the repository/service layer pattern?Harar
@chobo2: They are complementary. The UOW is intended to be domain- and persistence-ignorant. In fact, if you look around the web for information on using the repository pattern with NHibernate you will probably see many of them advocating the UOW at the same time. Technically the ISession is supposed to be considered a unit of work, but... kinda not really. A true UOW saves all your work when you commit it and discards all of it if you don't; the session saves it anyway but is just totally nondeterministic about when.Remiss
Do you have any good tutorials that show all of these. I been looking and still kinda confused how everything will look(what do I need to ninject, how would a simple method in my service layer look like with unit of work, how does the repository work? Does it know anything about the unit of work?)Harar
@Harar is there something wrong with the link that was posted? It has a usage section. Just don't use Current when you're doing DI, instead inject the IUnitOfWork.Remiss
I feel I am learning more about TDD than actually the unit of work with all the unit tests and mockups he is making. So I find I am losing focus on what I actually want to learn.Harar
I been trying around with the unit of work and repo pattern. You can see it here. #7113745 not sure if I am on the right track thoughHarar

© 2022 - 2024 — McMap. All rights reserved.