how to increase web app performance with asp.net mvc3 and nhibernate

asp.net-mvc, c#, nhibernate

Solution

A lot of details are missing, but since you say this is your first NHibernate app, i'm going to recommend the likely things to check:

- Create NH SessionFactory once at application start (as @Rippo is getting at). SessionFactory is expensive to create. Do it in Application_Start()

- Open a new NH Session for each web Request. Once the Request is over, throw it away. NH ISession are cheap/quick to create. Generally, it's bad practice to reuse or cache ISession for a long time. For a simple implementation, you can do it in your Controller if you like, since that only lives per request.

- When querying (NH LINQ? QueryOver? what do you use), be sure to limit the records returned. Don't .ToList() the whole table and just show 20. Use Skip/Take.

- Watch out for the SELECT N+1 problem. This can kill your performance on any OR/M.

Those would be my recommendations, code sight-unseen.

Update: so the primary problem seems to be the 10-15 sec startup, which is likely the SessionFactory initialization time during Application_Start.

I have not tried it yet, but the general recommendation to have quick startup times is to serialize the NH Configuration object to disk (which contains mappings), and load that on each startup (a primitive cache). If the mappings change, you would need to detect that, or else do a manual load (delete the serialized configuration file).

In your code, you are using Fluent NHibernate to build a FluentNHibernate.Cfg.FluentConfiguration instance, and call cfg.BuildSessionFactory() on it to return the new ISessionFactory. The Configuration you need to serialize is NHibernate.Cfg.Configuration. So you would probably modify your code to something like this:

    public static ISessionFactory CreateSessionFactory()
    {
        string conStringName = "ConnectionString";

        // http://weblogs.asp.net/ricardoperes/archive/2010/03/31/speeding-up-nhibernate-startup-time.aspx
        System.Runtime.Serialization.IFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

        NHibernate.Cfg.Configuration cfg = null;

        if (File.Exists("Configuration.serialized"))
        {
            using (Stream stream = File.OpenRead("Configuration.serialized"))
            {
                cfg = serializer.Deserialize(stream) as Configuration;
            }
        }
        else
        {
            // file not exists, configure normally, and serialize NH configuration to disk
            cfg = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                .ConnectionString(c => c.FromConnectionStringWithKey(conStringName)))
                .Mappings(m => m.FluentMappings.Add<Entity1>())
                .Mappings(m => m.FluentMappings.Add<Entity2>())
                .Mappings(m => m.FluentMappings.Add<Entity3>())
                .ExposeConfiguration(p => p.SetProperty("current_session_context_class", "web"))
                .BuildConfiguration();

            using (Stream stream = File.OpenWrite("Configuration.serialized"))
            {
                serializer.Serialize(stream, cfg);
            }
        }

        return cfg.BuildSessionFactory();
    }

This would cache the configuration to disk, so your app startups will be fast. Of course, when you change the NH mappings, you would have to detect that and reload the Fluent Configuration, or else manually delete the cache file.

A couple other tuning comments:

- You have .Mappings(m => m.FluentMappings.Add()).Mappings(m => m.FluentMappings.Add()) , etc. Just guessing here, but adding one-by-one may be creating multiple HBM files under the hood. You could try adding the mappings from an external assembly, and use .Mappings(M => M.FluentMappings.AddFromAssemblyOf())

really you should not do SessionFactory.OpenSession() in Application_Start(). Just create the SessionFactory there, and access SessionFactory.GetCurrentSession() in your code. Your global.asax should have:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    // we open one NH session for every web request, 
    var nhsession = SessionFactory.OpenSession();
    // and bind it to the SessionFactory current session
    CurrentSessionContext.Bind(nhsession);
}

protected void Application_EndRequest(object sender, EventArgs e)
{
    // close/unbind at EndRequest
    if (SessionFactory != null)
    {
        var nhsession = CurrentSessionContext.Unbind(SessionFactory);
        nhsession.Dispose();
    }
}

That would be the way to do session-per-request.

Problem

I'm develop my first application using mvc3 nhibernate orm layer with mssql db. This is my first application created with using nhibernate and everything is fine except intiial responsn time. After some investigations I'm implemented session per web request, which is definitely an upgrade, my entities are loaded much faster after first call, but my problem remains the same. Initial response time is really slow, when I type domainname.com and hit enter lwaiting time is approx. 10-15 sec. and this is not actual loading time of content, after that time 10-15 sec. my site is starts to load, few more sec. Is that time that session factory must init all "stuff" that needs but I tnink it must be something else. This is unacceptable. My app is running on winhost on Site Memory Allocation 200 MB, so I think this is not the problem. Any hints are welcome. If you need more details please ask. Thanks Update: After examing application session usage with nhibernate profiler I found some interesting stuff. Since I'm really a begginer in using profiler I think I found expensive session. IN general statistics 67 entities are loaded in 36.571 duration in seconds. This seconds value is really strange cause I have 10-max 15 sec to load. Second update: global.asax ``` public class MvcApplication : System.Web.HttpApplication{ public static ISessionFactory SessionFactory = MyDomain.Infrastructure.SessionProvider.CreateSessionFactory(); //My session factory is open in Application_Start() like this SessionFactory.OpenSession(); } ``` I'm using fluent approach in mapping my objects. So my session provider in domain project looks like this ``` //This should be used from web app, global.asax.cs calls public static ISessionFactory CreateSessionFactory() { string conStringName = "ConnectionString"; var cfg = Fluently.Configure() .Database(MsSqlConfiguration.MsSql2008 .ConnectionString(c => c.FromConnectionStringWithKey(conStringName))) .Mappings(m => m.FluentMappings.Add<Entity1>()) .Mappings(m => m.FluentMappings.Add<Entity2>()) .Mappings(m => m.FluentMappings.Add<Entity3>()) .ExposeConfiguration(p => p.SetProperty("current_session_context_class", "web")) .BuildConfiguration(); return cfg.BuildSessionFactory(); } ``` Update 3 Still no solution for this problem Update 4 and final My problem is definitilly in sessionFactory. I think that my configuration object should be serialized. If anyone can be kind enough to show how to do it using my code showed here with fluently conf. I will gladlly accept his/her answer. Thanks.

Original source

Related problems