Click here to Skip to main content
Click here to Skip to main content

NHibernate second level caching implementation

By , 7 Feb 2013
 

Introduction  

In this article, I have tried to explain step by step how NHibernate second level caching can be implemented. When NHibernate is used in any project as an Object Relational mapper, and developers are trying to improve performance based on NHibernate caching features, this will be very useful for them. The people who use Nhibernate must know the 1st level cache and its scope and limitations. Second level cache will help to overcome first level cache limitations and optimize application performance. Anyone who is also interested to know how second level caching can be implemented also helps them.

Background   

Entity caching is a very important technique to improve performance of any application. Sometimes we use caching layer before repository/data access layer for cache entity. Sometimes we cache it in our web context with the help of ASP.Net web component like Session, Application, Cache for store our entity/entity collection. 

If anyone use NHibernate for its dataaccess/Repository layer then NHibernate second level caching is very helpful for them and no need to cache Entity/Entity collection to other layers. It provides various caching and its expiration policy. I try to explain it step by step though any one can use it to their application and get benefited.

Must needed components     

If anyone wants to use NHibernet in their applications, then some components need to add as a reference of his projects. The components are 

    1.  NHibernate  

     2. Lesi.Collections 

If you prefer fluent configuration then you can take reference of another 

     3. FluentNHibernate   

These are the core components for NHibernate.    

Second level cache providers  

Next if I want to use second level cache then first need to add a provider for that. 

The popular providers are   

MemCache  

Velocity 

SysCache 

SysCache2    

If prefer SysCache2 because it supports database change notification. If your application need distributed cache then you can check with Velocity cache provider.  Adding SysCache2 component reference, I prefer NuGet package cmd lets.  You can use  

Install-Package NHibernate.Caches.SysCache2 

cmd let from Package Manager. It will download/add reference to your projects. Remember that it will not add reference of FluentNHibernate component. You manually need to add that.  

Database configuration   

 If I want to take database change notification you need to setup sql server 2005 or higher version (In theory I found sqlserver 2000 supports it, but I never tried that). Next we need to enable Sql server Service broker. TSQL command is 

ALTER DATABASE <databasename> SET ENABLE_BROKER; 

Just for your information, you can enable service broker in a database only once. We can also disable broker if you need. TSQL command is  

ALTER DATABASE <databasename> SET DISABLE_BROKER; 

Next you need to take help of aspnet_regsql tool.  

You need to execute command from visual studio command prompt  

aspnet_regsql  –S <sqlserver instance name> -U <database username> -P <database user password> -d <database name> -ed 

If you write aspnet_regsql /? In visual studio command prompt then you know the details of this command. 

If you first run sql server profiler then execute that command then you can see that it will create AspNet_SqlCacheTablesForChangeNotification table and 5 stored procedures

  1. AspNet_SqlCachePollingStoredProcedure
  2. AspNet_SqlCacheRegisterTableStoredProcedure  
  3. AspNet_SqlCacheUpdateChangeIdStoredProcedure
  4. AspNet_SqlCacheUnRegisterTableStoredProcedure
  5. AspNet_SqlCacheQueryRegisteredTablesStoredProcedure  

and one Database user role named aspnet_ChangeNotification_ReceiveNotificationsOnlyAccess.    

Next you need to register which tables you want to allow change notifications. The command is

Aspnet_regsql  –S <sqlserver instance name> -U <database username> -P <database user password> -d <database name> -t <tablename> -d <databasename> -et   

You can create a single batch (.bat) file with all the necessary commands listed above. Then you will have a simpler deployment procedure.  

Prepare web/app config file  

Anyone can think that NHibernate second level cache is used only web context. But it is not true. You can easily use that feature in your Console, Windows Service application too. 

Register syscache2 section inside <configSections> node 

<configSections>
<section  name="syscache2" type="NHibernate.Caches.SysCache2.SysCacheSection, NHibernate.Caches.SysCache2" requirePermission="false"/>   
</configSections>   

Register sqlCacheDependency inside <system.web> node. If you use App.config then you need to add <system.web> node under <configuration> node and also add a reference to the System.Web component. SqlCacheDependency object actually reside in this component and it is .NET framework component. So it will work fine in your application that does not have any web context.  

 <system.web>
    <caching>
      <sqlCacheDependency enabled="true" pollTime="1000">
        <databases>
          <add name="db2" connectionStringName="db"/>         
        </databases>       
      </sqlCacheDependency>     
    </caching>   
  </system.web> 

Why do we need to register sqlCacheDependency? Actually syscache2 expire its cache based on databasae change notification (Well, not always but, only when you configure the "regions" that way).  SqlCacheDependency object is actually managing it and SysCache2 uses it. One important attribute is pollTime (in milliseconds). That means when it goes to database to check if there are any changes. 

SysCache2 region register to the web/app config. <syscache2> node will be registered under <configuration> node.

<syscache2>
    <cacheRegion name="tableDependency" priority="High">
      <dependencies>
        <tables>
          <add name="one" databaseEntryName="db2" tableName="MyTable1" />         
        </tables>       
      </dependencies>      
    </cacheRegion>   
  </syscache2>

You can see that there is a node  "cacheRegion" named “tableDependency”. CacheRegion is actually an independent cache expiration policy. From our Entity Mapping configuration, we can refer that policy. databaseEntryName attribute of <tables> node refer <add name=db2> under <databases> &<sqlDependency> node. This databaseEntryName attribute sometimes create confusion and that is, what will be its value. So carefully we should set that value. 

 Another <cacheRegion> is registered for cache expiration on certain time 

<syscache2>
<cacheRegion name="ExpireAfterCertainTime" timeOfDayExpiration="22:25:00" priority="High"/>
</syscache2>   

The above region reference cache will be expire 08:00:00 PM. The attribute value should be  24 hour time format.   

Another <cacheRegion> is registered for cache expiration on certain time interval.

<syscache2>
      <cacheRegion name="FiveSecondTimeInterval" relativeExpiration="5" priority="High"/>
</syscache2> 

The above region reference cache will be store in cache in 5 seconds. After that period it will automatically expire.  

 Create NHibernate Session Factory and Open Session   

In NHibernet we need to create a SessionFactory class and with the help of that class we can open a new session and communicate database. Create a SessionFactory is little costly and we can create it as a singletone fession. SessionFactory create code like as follows

private static ISessionFactory _sessionFactory;
public static ISessionFactory GetSessionFactory()
{
    //The code is not thread safe.
    if (null == _sessionFactory)
    {
        FluentConfiguration nhConfig = Fluently
            .Configure()
            .Database(MsSqlConfiguration
                .MsSql2008.ConnectionString(c => c
                    .Database(_conn.InitialCatalog)
                    .Server(_conn.DataSource)
                    .Username(_conn.UserID)
                    .Password(_conn.Password)
                )
            )
            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Developer>()
            .Conventions.Add(DefaultLazy.Never()));
        nhConfig.Cache(c => c.ProviderClass<SysCacheProvider>().UseSecondLevelCache());
        _sessionFactory = nhConfig
            .ExposeConfiguration(v => new SchemaExport(v).Create(false, false))
            .BuildSessionFactory();
    }
    return _sessionFactory;
} 

And we can get open session like the following code like the following code 

public static ISession CreateSession()    
{   ISessionFactory factory = GetSessionFactory(); 
   return factory.OpenSession();         
}  

If you want to enable/disable second level cache based on configuration then you need to create SessionFactory object like as follows  

public static ISessionFactory GetSessionFactory()
{
    FluentConfigurationnhConfig = Fluently.Configure()
                    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(
                        c => c.Database(conn.InitialCatalog)
                            .Server(conn.DataSource)
                            .Username(conn.UserID).
                            Password(conn.Password)))
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Organization>()
                            .Conventions.Add(DefaultLazy.Never()));

    if (Config.IsSecondLevelCachingEnabled)
        nhConfig = nhConfig.Cache(c => c.ProviderClass<SysCacheProvider>()
            .UseSecondLevelCache());

    _sessionFactory = nhConfig.ExposeConfiguration(v => new SchemaExport(v)
        .Create(false, false)).BuildSessionFactory();

    return _sessionFactory;
}

Entity mapping modification for support second level cache   

Without cache configuration inside entity mapping classes, second level cache would not work. So be careful about that. Now the question is how we can do that? See the code bellow. Here I use fluent configuration.  

It is Developer class  

 public class Developer 
 { 
        public virtual int Id { get; set; }
        public virtual string FullName { get; set; }
        public virtual Department Department { get; set; }
        public Developer(){}  
 } 

And Developer class’s fluent mapping class like as follows

public class DeveloperMap : ClassMap<Developer>
{
    public DeveloperMap()
    {
        base.Cache.NonStrictReadWrite().Region("FiveSecondTimeInterval");
        //base.Cache.NonStrictReadWrite().Region("ExpireAfterCertainTime");              
        //base.Cache.NonStrictReadWrite().Region("ExpireAfterCertainTime");
        //base.Cache.NonStrictReadWrite().Region("tableDependency");
        base.Table("Developers");
        base.Id(c => c.Id).Column("Id");
        base.Map(c => c.FullName).Column("FullName");
        base.References(c => c.Department).Column("DepartmentId");
    }
}  

In the above code the DeveloperMap’s inside constructor, first line declare that it need to support Cache and configure its cache  expiration policy based on Region declaration. There are many types of Cache usage pattern are supported for example ReadOnly, NonStrictReadWrite etc. If you search on web, you can find more information regarding that. 

Summary      

NHibernate second level caching is a very unique feature and it is very helpful to improve application performance. Actually it is a matter of cache like other caching, so before apply that, you must carefully think and plan against entities which you need to support second level cache and what will be its expiration policy. If you make mistake to choose proper entities to cache and its expiration policy then it will not provide any benefits, more over this will create many bugs.  So again before plan/configure your entities for taking advantage from second level cache, spend times for rethink and revised that (helpful to share it to your team and take feedback). 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

S. M. Ahasan Habib
Software Developer (Senior) The Jaxara IT Ltd.
Bangladesh Bangladesh
Member
I have 10+ years industry experience mostly Microsoft technologies. My expertise ares are Software architecture, design, use various design pattern to real life implementation, Unit testing, Performance tuning, Security, Java script, Ajax, ASP.NET Webform/MVC, SQL Server, NHibernet etc.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5 PinmemberSavalia Manoj M14 Feb '13 - 14:42 
Questionvery nice PinmemberCIDev13 Feb '13 - 4:35 
GeneralMy vote of 5 Pinmembermostafiz rahman3 Feb '13 - 16:23 
GeneralMy vote of 5 PinmemberSavalia Manoj M21 Jan '13 - 2:49 
GeneralMy vote of 5 PinmemberSk. Tajbir18 Jan '13 - 0:18 
GeneralMy vote of 5 PinmemberSOH3L17 Jan '13 - 19:09 
GeneralMy vote of 5 PinmemberTanvirRaihan17 Jan '13 - 19:07 
GeneralNice and helpful article PinmemberAhsan Murshed17 Jan '13 - 18:29 
GeneralMy vote of 5 PinmemberMember 452258717 Jan '13 - 16:58 
GeneralMy vote of 5 PinmemberKhorshedAlam17 Jan '13 - 16:58 
GeneralMy vote of 5 Pinmemberzp bappi17 Jan '13 - 15:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130513.1 | Last Updated 7 Feb 2013
Article Copyright 2013 by S. M. Ahasan Habib
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid