Why I use the NHibernate Profiler for optimal ORM performance

In this post I want to show how the NHibernate Profiler can help you avoid ORM performance issues as discussed in the post ‘ORMs don’t kill databases. Developers do.‘.

While it’s not open source it’s definitely worth the investment if you use NHibernate because it can give you alerts about any potential issues and information about how to resolve them.

Something that a profiling tool like SQL Profiler just can’t do.

For example by running the code below to insert 200 apples and then retrieve them from the database

Note: this code is meant to be written like this to generate alerts

[Test]
public void NHibernate_Profiler_Demo()
{
    using (IStatelessSession session = FruitDatabase.SessionFactory.OpenStatelessSession())
    {
        using (ITransaction txn = session.BeginTransaction(IsolationLevel.ReadCommitted))
        {
            for (int i = 0; i < 200; i++)
            {
                var apple = new Fruit() { Name = "Apple" };
                session.Insert(apple);
            }
            txn.Commit();
        }                   
    }
    using (IStatelessSession session = FruitDatabase.SessionFactory.OpenStatelessSession())
    {                              
            var results = session.Query<Fruit>().ToList();                                           
    }
}

the NHibernate Profiler gives a warning about the ‘large number of individual writes’ and a suggestion about having ‘too many database calls per session’.

As you can see in the screenshot there are links to read more which takes you to a takes you to a page with helpful tips to resolve issue and a link to ignore the alert (not recommended!).

This isn’t the only alert that is raised when writing, so you should repeat the cycle of test and fix until no further alerts are raised, the queries/execution plan and/or code have been optimized e.g. changing how/where identifiers are created.

NHibernate Profiler alerts about reads

In addition to detecting problems when writing data the NHibernate profiler can give you tips when reading data.

For the example in this post there are alerts for an unbounded result set and an implicit transaction. Again these probably won’t be the only alerts raised.

Additional features in the NHibernate Profiler include being able to see the result of the query and the execution plan, the ability to compare sessions and a general overview of how an application is interacting with a database.

Check out this live HTML Demo to give it a test run.

There are also versions available for other ORMs like Entity Framework, unfortunately you’ll have to buy a separate licence for each one.

So while you could write your own solution to log and analyse interactions between your app and the database, the NHibernate Profiler will save you so much time because it can do the investigative work for you.

ORMs don’t kill databases. Developers do.

Following a recent post I wrote Phillip Haydon made an excellent argument about not using LINQ in ORMs like nHibernate and Entity Framework without consideration to what they are doing.

Something I definitely agree with.

Just as guns don’t kill people, ORMs don’t kill databases. Developers do.

I should have picked up on this because it’s happened to me on a project I was working on.
Death by ORM
During development everything was performing well. So well that even the most sceptical developers decided to use ORMs instead of stored procedures.

The application was responsive and users were happy.

However in production there has no happy ending. The application went from running like sh!t off a proverbial shovel to just sh!t.

It was a nightmare and the finger pointing began.

The problem was we assumed (always a bad thing) the queries generated by the ORM would be as good as if we had written them ourselves.

So when we looked at what was being generated it came as a bit of a shock to see what was going on.

But while it was easy to use the ORM as a scapegoat the reality was developers were at fault.

Here’s a simple example of how even the smallest of changes can make a difference in the SQL ORMs generate.

In the code below all we want nHibernate to do is select the of the fruit that is NOT also a color.

[Test]
public void Tale_Of_Two_Queries()
{
    var fruitDatabase = new FruitDatabase();
    using (ISession session = fruitDatabase.Session)
    {
        using (ITransaction txn = session.BeginTransaction())
        {
                
            var apple = new Fruit() { Name = "Apple", IsAlsoAColor = false};
            var orange = new Fruit() { Name = "Orange", IsAlsoAColor = true };

            session.Save(apple);                    
            session.Save(orange);                    
            txn.Commit();
        }

        var firstQuery = session.Query<Fruit>()
        .Where(f => !f.IsAlsoAColor)
        .Select(f => f.Name)
        .ToList();        

        var secondQuery = session.Query<Fruit>()
                .Where(f => f.IsAlsoAColor == false)
                .Select(f => f.Name)
                .ToList();                 
    }
}

For the query that uses !f.IsAlsoAColor the SQL query looks like this

select fruit0_.Name as col_0_0_
from   Fruits fruit0_
where  not (fruit0_.IsAlsoAColor = 1)

For the query that uses f.IsAlsoAColor == false the sql generated looks like this

select fruit0_.Name as col_0_0_
from   Fruits fruit0_
where  case 
         when fruit0_.IsAlsoAColor = 1 then 1
         else 0
       end = case 
               when 0 /* @p0 */ = 1 then 1
               else 0
             end

While this is a trivial example it highlights the fact you don’t know what the generated SQL will be, let alone the execution plan.

And if your query returns the results you want are you really going to spend time finding out what’s going on ‘under the hood’?

How comes nobody talks about this?

I can’t ever recall a presenter warning about this during any ORM talk I’ve been to. The focus always on what it can do and how it compares to other ORMs rather than best practices.

It’s not just a .Net problem either

After talking to Ruby on Rails developers this is a problem for them too, because Active Record doesn’t work 100% of the time. When it goes wrong they have to implement workarounds to execute queries without using active record.

So how can I make sure it doesn’t happen to me?

The first thing is to know what issues to look out for. The nHibernate profiler alerts page is an excellent resource for this.

With this information you can use profiling tools to see what queries your ORM is generating and how your application is performing.

My last tip is to always use the best data access method for what you’re doing. It’s easy to become complacent or aim to everything using an ORM, but if it makes more sense to use a stored procedure then that’s what you should do.

It would be good to hear what your experiences are with issues like this and to share any tips/tools you have used to avoid ORM performance issues.

In the meantime I’ll leave you with this excellent DBA vs. Developer (Star Wars Style) video

How to deploy ASP.NET MVC applications to AppHarbor

I’ve already blogged about how awesome AppHarbor is.

So to back up my opinion I’ve decided to write a tutorial about how to deploy an ASP.NET MVC application to AppHarbor.

The application we’ll be deploying will be the ASP.NET MVC Music Store (created by James Senior, Jon Galloway and Scott Hanselman) which uses ASP.NET MVC 3 with Razor syntax and data access via Entity Framework 4.

ASP.NET MVC MusicStore Homepage

Click here to a deployed version of the ASP.NET MVC Music Store running on AppHarbor.

To follow along with the tutorial you’ll need to have the following installed:

If you’re also going to use the ASP.NET MVC Music Store please ensure you change the ..\Views\Shared\_Layout.cshtml to include HTML to indicate the site is a sample available from codeplex and a noindex robots meta tag.

<!DOCTYPE html>
<html>
<head>
    <meta name="robots" content="noindex,nofollow" />
    <title>This is a ASP.NET MVC sample application which can be downloaded at http://mvcmusicstore.codeplex.com</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" 
        type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" 
        type="text/javascript"></script>
</head>
<body>
 <p style="padding:20px;background-color:#000;color:#FFF;text-align:center">
        This is a ASP.NET MVC sample application which can be downloaded at <a href="http://mvcmusicstore.codeplex.com">http://mvcmusicstore.codeplex.com</a>
    </p>
    <div id="header">
        <h1>
            <a href="/">ASP.NET MVC MUSIC STORE</a>
        </h1>
       <ul id="navlist">
           <li class="first"><a href="@Url.Content("~")" id="current">Home</a></li>
           <li><a href="@Url.Content("~/Store/")">Store</a></li>
           <li>@{Html.RenderAction("CartSummary", "ShoppingCart");}</li>
           <li><a href="@Url.Content("~/StoreManager/")">Admin</a></li>
       </ul>
    </div>
    @{Html.RenderAction("GenreMenu", "Store");}
    <div id="main">
        @RenderBody()
    </div>
    <div id="footer">
        built with <a href="http://asp.net/mvc">ASP.NET MVC 3</a>
    </div>
</body>
</html>

Let’s get started

  1. How to add a ASP.NET MVC solution to a Git repository
  2. How to push a ASP.NET MVC Git repository to AppHarbor
  3. How To Create a SQL Server Database in AppHarbor
  4. How to configure ASP.NET MVC to use a SQL Server database in AppHarbor

I can browse around the store and add things to my cart but can’t log in as an administrator

That’s because you can only have one database per application… something I didn’t know when I started this series.

As AppHarbor doesn’t support file storage (yet) you can’t use the ASPDBNET.MDF database which the music store uses for ASP.NET Membership. See this AppHarbor support entry for more details.

Only One AppHarbor Database Is Allowed



You can of course workaround this by adding membership tables etc. to the existing SQL Server database by via an alternative membership provider which includes using another application hosted on AppHarbor.

Page 1 of 212