static void

NHibernate Caching

There are two levels of caching.

First level cache

First level cache is the session's identity map of entities - it is local to the session and destroyed with it. StatelessSessions don't use it.
session.Get and session.Load check the first level cache (Get will save a database hit).

Second level cache

Second level cache is common to all sessions (in asp.net terms, equivalent to Cache or Application objects). It doesn't cache the entities, only a map of ids and property values.

Providers

After you've referenced the provider, you need to configure it.

Configure second level cache=true

For Syscache and other providers, you have priorities 1-5 (low to high, 3 is normal) and can specify "regions" with different expirations.

In config xml:

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
  <session-factory>
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
    <property name="connection.connection_string_name">Northwind</property>
    <property name="current_session_context_class">web</property>
    <property name="show_sql">true</property>
    <!-- use caching -->
    <property name="cache.provider_class">NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache</property>
    <property name="cache.use_second_level_cache">true</property>
    <property name="cache.use_query_cache">true</property>
  </session-factory>
</hibernate-configuration>

In Fluent NHibernate:

public static ISessionFactory FluentlyConfigure()
{
    return Fluently.Configure()
        //which database
        .Database(
            MsSqlConfiguration.MsSql2008
        //connection string from app.config
                .ConnectionString(
                    cs => cs.FromConnectionStringWithKey("Northwind"))
                .ShowSql())
        //2nd level cache
        .Cache(
            c => c.UseQueryCache()
                .UseSecondLevelCache()
                .ProviderClass<NHibernate.Caches.SysCache.SysCacheProvider>())
        //find the mappings
        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CategoryMapping>())
        .BuildSessionFactory();
}

Configure classes

You can cache an entire class (e.g. a reference class that seldom changes).
Caching can be read-only, read-write (NB: doesn't lock, doesn't work in a webfarm and only on transaction.Commit) and nonstrict-read-write (expect stale data)
For read-only caches, the classes should be mutable=false
Important: if the cached classes have lazy associations, you can easily get N+1 SQL being fired on every session. Either don't map the associations, or cache the entire object graph.

In hbm.xml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Northwind" assembly="Northwind">
  <class name="Category" table="`Categories`" schema="`dbo`" mutable="false">
    <!-- read-only, read-write, nonstrict-read-write -->
    <cache usage="read-only" region="Hour" />

In Fluent NHibernate:

public CategoryMapping()
{
    Table("Categories");
    Cache.ReadOnly();
    ReadOnly(); //mutable=false

Remember that use_second_level_cache=true only means session.Get and session.Load use the cache. For queries to use the cache, you must set use_query_cache=true.

Query caching

Common queries can also be cached (specifically, it stores the result ids- so you could easily have a select N+1 here if the class is not cached).
Remember to set use_query_cache=true

var activeProducts =
   session.Query<Product>()
    .Where(x => x.Discontinued == false)
    .Cacheable();
 
var activeCriteria =
    session.CreateCriteria<Product>()
        .Add(Restrictions.Eq("Discontinued", false))
        .SetCacheable(true)
        .List<Product>();