5

A have an action on my MVC application that have an id and returns a person's name.

What is the best practice for that? I'm following NHProf tips, but code sounds a little strange or something for me.

using (var session = Helper.SessionFactory.OpenStatelessSession())
{
    using (var tran = session.BeginTransaction(IsolationLevel.ReadCommitted))
    {
        return session.Query<Person>().Where(x => x.Id == id).Select(x => x.Name).SingleOrDefault();
        tran.Rollback();
    }
}
AGB
  • 2,230
  • 1
  • 14
  • 21
Zote
  • 5,343
  • 5
  • 41
  • 43
  • I can't understand why it suggest a transaction... – Felice Pollano May 10 '12 at 12:29
  • 1
    I would not use `OpenStatelessSession`the stateless session is for bulk scenarios and ignores the L1 cache. Instead of doing a linq query I would simply call `.Load(1)` or `.Get(1)` which expresses intend more than a linq query. – Andreas May 10 '12 at 18:12

3 Answers3

4

The NHProf alert page explains it quite well I think -

http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions

Basically it's saying if you don't manage transactions yourself, the database will create an "implicit transaction" and auto-commit for every statement, including queries. The misconception is that transactions are useful only for insert / update operations.

In your example above, it's not much of an issue as your transaction only executes a single statement anyway. If your method was running several statements however it would be good practice to wrap them in a transaction.

richeym
  • 4,049
  • 3
  • 24
  • 23
  • OK, but for simple methods should I follow NHProf and create a transaction or not? – Zote May 10 '12 at 13:21
  • 1
    The answer is only partial true, you should __always__ warp your database query in an transaction and indeed it would be an issue even if you execute a single statement. Implicit transactions are expensive! and NH won't use the L2 cache without a transaction. – Andreas May 10 '12 at 18:05
0

The following is how I would approach this select:

    using (var session = Helper.SessionFactory.OpenStatelessSession())
    using (var tran = session.BeginTransaction(IsolationLevel.ReadCommitted))
    {
        try
        {
            string personName = session.Query<Person>()
            .Where(x => x.Id == id)
            .Single(x => x.Name);

            tran.Commit();
            return personName;
        }
        catch(Exception ex)
        {
            // handle exception
            tran.Rollback();
        }
    }

This SO answer gives good advice to dealing with transaction commits:

NHibernate - Is ITransaction.Commit really necessary?

With regards to your LINQ this is an interesting article on how not to approach queries using the extension method style syntax:

http://compiledexperience.com/blog/posts/how-not-to-use-linq

Community
  • 1
  • 1
Peadar Doyle
  • 1,064
  • 2
  • 10
  • 26
  • -1 for catching everything, doing redundant work (using and try catch). – Andreas May 10 '12 at 17:58
  • @Andreas as far as I'm aware using has no way of processing a thrown exception and the choice is to use a redundant try or manually disposing. Also how would you implement error handling in this case since there is a number of layers that could throw an error (NHibernate, ADO.NET, LINQ). – Peadar Doyle May 10 '12 at 22:24
  • 2
    http://stackoverflow.com/questions/6418992/is-it-a-better-practice-to-explicitly-call-transaction-rollback-or-let-an-except and for exceptions http://stackoverflow.com/questions/426346/is-this-a-bad-practice-to-catch-a-non-specific-exception-such-as-system-exceptio http://stackoverflow.com/questions/114658/catching-base-exception-class-in-net http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx and don't tell me that you can properly handle stackoverflows, out of memory exc with your `tran.Rollback();`. – Andreas May 11 '12 at 07:55
  • 2
    Thanks for the links, I wasn't aware that transactions were implicitly rolled back. Also I should have put the "//handle exception" after Rollback() since I intended it as a place holder for error handling. I appreciate what you're saying with regards to catch all exception handling and it probably isn't the best thing to do here. – Peadar Doyle May 11 '12 at 08:26
0

do not create multiple sessions over one HTTP request, ideally what you need to do is open a session and a corresponding transaction at the request scope and use this session throughout all your actions.

Here is a blogpost explaining how to achieve it: http://hackingon.net/post/NHibernate-Session-Per-Request-with-ASPNET-MVC.aspx

I would suggest the use of an IOC container and create a class that creates the session for you and scope this class to the request scope.

There are lots of resources on the web to approach this problem.. Google it..

Baz1nga
  • 15,485
  • 3
  • 35
  • 61
  • While one session per request is sound advice, it's not a hard and fast rule, and having one transaction per request is bad advice, plain and simple. – Spivonious Jul 22 '16 at 16:41