Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

NHibernate Second Level Caching Implementation

4.85/5 (31 votes)
21 Dec 2013CPOL7 min read 108.1K   788  
In this article, I tried to explain step by step, how Nhibernate second level caching can be implemented

Table of Contents

1. Introduction

intro

In this article, I explain how NHibernate 2nd level cache is implemented. As an Object Relational Mapper (ORM) when NHibernate is used often we see developers try to improve performance using NHibernate caching feature. In this article I focus on this area where 2nd level cache plays rule for better performance. Second level cache will help to overcome 1st level cache limitations. Anyone who are also interested to know how 2nd level caching can be implemented in any project, this article is for them too .

2. Prerequisite

  • NHibernate basics
  • 1st level caching

3. Background

Entity level caching is a very important technique and it improves performance of application. Occasionally we introduce caching layer in n-layer architecture which exists just before data access layer. The main responsibility of this layer is cache business and supply business entities to the business layer from its cache when business layer demands.

CacheLayer

Sometimes we cache entities in web context using ASP.NET web components like:

  • ViewState,
  • Session,
  • Application,
  • Cache object

When anyone use NHibernate and its 2nd level caching feature then further it will not be needed to cache entities in other layers. NHibernate provides various caching and its expiration policy. I try to explain it in a very easy way so that any one can easily use it in their application.

4. Prerequisite Components

If anyone wants to use NHibernet in his applications, then few components must be needed and he should add as a reference of those components to his projects. Components are:

  1. NHibernate
  2. Lesi.Collections

If you prefer fluent configuration (I prefer fluent mapping configuration over xml mapping configuration), then you can take reference of another:

  1. FluentNHibernate

These are the core components of NHibernate.

5. 2nd Level Cache Providers

Image 3

If I want to use 2nd level cache feature, at first I need to add at least one 2nd level cache provider reference to my project.

Many cache providers are exists in market. Few popular providers are:

  • MemCache
  • Velocity
  • SysCache
  • SysCache2
  • Hash Table
I prefer SysCache2 because it supports database change notification. If your applications need distributed cache, then you can check with Velocity. For adding SysCache2 component reference to my project, I prefer NuGet package cmd lets. You can use:
Install-Package NHibernate.Caches.SysCache2 

cmd let from Visual Studio's Package Manager window. It will download/add reference syscache2 assembly to your projects.

Image 4

Note that it will not add reference of FluentNHibernate component. You manually need to add that.

6. Configuration

config

After adding component reference you need to configure your

  1. Database and
  2. Application

so that it can work perfectly.

6.1. Database Configuration

If you want to take database change notification service then you need to setup SQL Server 2005 or higher version (I found various online sources there said SqlServer 2000 also supports it, but never tried that). Next, we need to enable SQL Server Service Broker feature. TSQL statement is:

ALTER DATABASE <databasename> SET ENABLE_BROKER; 

Note that, you can enable service broker feature in a database for only once. We can also disable service broker feature at any time. TSQL statement is:

ALTER DATABASE <databasename> SET DISABLE_BROKER; 

Next, you need to use 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 execute aspnet_regsql /? command from Visual Studio command prompt, then you know the details of this command and its options.

If you first open SQL Server Profiler, then execute the above tsql statement, then you will see that it creates AspNet_SqlCacheTablesForChangeNotification table with 5 stored procedures(SP).

Procedures are:

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

and also it will create one database user role named aspnet_ChangeNotification_ReceiveNotificationsOnlyAccess

Next, you need to register tables that 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> -et <sql cache dependency tables>     

You can create a single batch (.bat) file with all the necessary commands listed above. It will make your life more easier.

6.2. Application Configuration

You may think that NHibernate 2nd level cache is worked with only in web context. But it is not true. You can easily use that feature in your Console, Windows Service application, Windows Application too.

In your configuration file (if web application then it will be web.config, if other then web then it will be app.config) you need to register syscache2 section inside <configSections> node:

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

then you need to 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 resides in this component and it is .NET Framework component. For that reason it will work 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>

Q. Why do we need to register sqlCacheDependency?

A. Actually syscache2 expires its cache based on database change notification (Well, not always but, only when you configure the "regions" that way). SqlCacheDependency object is actually manage that and SysCache2 use it.

One important attribute is pollTime (in milliseconds). The value of pollTime indicate time interval when it visit database periodically to check if there are any changes.

SysCache2 region registers in 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 to that policy. databaseEntryName attribute of <tables> node refer to <add name=db2> under <databases> & <sqlDependency> node. This databaseEntryName attribute sometimes creates 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 on 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 stored in cache in 5 seconds. After that period, it will automatically expire.

7. NHibernate Session

In NHibernate, we need to create a SessionFactory class. Using that class we can open a new session and start communicating with database. Create SessionFactory object is costly and for that reason we should create it as a singleton session. That means per application context it will create ony one timme. SessionFactory object creation code sample:

C#
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 create and opened session like the following:

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

If you want to enable/disable second level cache by configuration, then instead of using above code you need to create SessionFactory object using following code:

C#
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;
}

8. Entity Mapping

Without cache configuration from entity mapping classes, 2nd level cache would not work. So be careful about that.

Q. How can we configure cache from entity mapping class?

A. See the code below. Here I use fluent configuration. If you want you can use xml based configuration too. 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:

C#
public class DeveloperMap : ClassMap<Developer>
{
    public DeveloperMap()
    {
        base.Cache.NonStrictReadWrite().Region("FiveSecondTimeInterval");
        //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 class's constructor, first line declares that it needs to support Cache and configure its cache expiration policy based on Region declaration. There are many types of Cache usage pattern that are supported, for example ReadOnly, NonStrictReadWrite, etc. If you search on the web, you can find more information regarding that.

9. Conclusion

NHibernate 2nd level caching is a very unique feature and it has capability to improve application performance. Point to be noted that it is a caching Technic similar to other caching. Before you apply it must carefully think and plan against entities which you need to support 2nd level cache and define its expiration policy. If you make a mistake to choose proper entities to cache and its best expiration policy, then it will not provide any benefits, moreover this may create many bugs. So before planning/configuring your entities for taking advantage from 2nd level cache, spend time to rethink and revise and verify and share it to your team members so that no error found in your plan/configuration/entity selection for cache.

License

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