Published: 22 Nov 2010
By: Dino Esposito

Where would you place caching in your layered solution? Two main patterns exist for caching in applications: Cache Aside and Cache Through.

Contents [hide]

The NHibernate Comparison of Entity Framework series

  • Part 1 A comparison on multiple database support in the Entity Framework and NHibernate O/RMs.
  • Part 2 Last month I took the challenge of trying to compare Entity Framework (EF4) and NHibernate (NH) in a hopefully unbiased and feature-driven way. In this article, I'll start looking into some programming features such as lazy loading.
  • Part 3 This article is about fetch plans-a recognized and common way for developers to instruct the O/RM about the structure of the SQL you desire.
  • Part 4 A Feature-driven Comparison of Entity Framework and NHibernate-2nd Level Caching
  • Part 5 A Feature-driven Comparison of Entity Framework and NHibernate - Self-tracking entities
  • Part 6 A Feature-driven Comparison of Entity Framework and NHibernate - Queries
  • Introduction

    Nowadays no form of data access makes any longer sense without some robust form of caching. The idea behind caching is surprisingly simple and effective: make frequently read data accessible in a way that is faster than getting it from the physical location. The .NET infrastructure offers a few options for caching. In ASP.NET applications the first option you run across is the built-in Cache object. It is fairly rich, serves most of the typical needs, but it is limited to a single server scenario. In .NET 4, the caching API has been moved out of the ASP.NET space and made available to just any applications. In addition, you have the Caching Application Block in the Enterprise Library. It goes beyond the basic caching API in that it offers a provider-based mechanism to cache data in various stores: in-memory, XML files, nearer databases.

    The real problem to address, however, is yet another. Where would you place caching in your layered solution? Two main patterns exist for caching in applications: Cache Aside and Cache Through.

    The Cache Aside pattern entails the developer manages the contents of the cache manually. This means that the caching layer is different from the data access layer (DAL) and it is checked before placing a call to the DAL. Invoked from the application logic, the code in the caching layer checks the data in the cache before invoking the DAL. The DAL has no awareness of the caching layer. The caching layer is responsible for evicting and updating the cache whenever the data source is updated.

    In the Cache Through pattern the cache is embedded in the DAL. The application logic just calls into the DAL and the DAL reads and writes data hiding the cache from the rest of the application. When the DAL is based on an O/RM tool, the theme of caching inevitably touches on O/RM tools. Let's find out more.

    Caching in O/RM Tools

    All O/RM tools provide some caching capabilities. Normally, caching in O/RM tools is articulated in two levels. The first level is usually a transaction-level cache of persistent data that lasts for the duration of the context object. The context object caches any data it happens to retrieve so that the same record won't be retrieved twice in the same session. The first-level cache is also known as the "identity map".

    The second-level caching operates down the stack below the first level of cache you typically have in the O/RM context object. The second level cache is intended to provide a cached database view so that queries can be resolved quickly on the client without hitting the database server at all. Whenever a piece of data is missing and the database server is contacted, the returned data is inserted into the cache too. Items are commonly removed from the cache based on some policies to keep the size under control.

    In the end, the first level caching works on a per session basis whereas the second-level caching stores data shared across multiple sessions.

    NHibernate and Entity Framework both support first-level caching out of the box. Second- level caching is usually offered through external cache providers created by third-party companies when not by the same product development group. Architecturally speaking, NHibernate and Entity Framework limit to provide injection points for adding a second-level cache. Entity Framework, however, doesn’t ship with any second-level caching provider; NHibernate, instead, ships with a few of them and others can be obtained via the community.

    Second-level Caching with Entity Framework

    As mentioned, you won't find a second-level cache integrated in Entity Framework 4. Does this mean that there's nothing you can do about it? Is Cache Aside the only way to go when you use Entity Framework in your DAL and business layer? The answer is yes, if you only want to rely on out-of-the-box and fully supported solutions. Instead, if you're ready to deal with some sample code not officially supported by Microsoft, or if you're excited about writing a custom wrapping provider for Entity Framework, well you have an alternate option.

    The basic fact is that Entity Framework doesn't currently offer a simple and easy injection point for you to plug in your cache provider. This indeed represents one of the major differences with NHibernate. It rather offers a generic mechanism to extend the capabilities of an existing data provider. You could write a wrapper for an existing data provider (for a given database) and add some ad hoc capabilities including caching, tracing, and whatever else you may think of. Figure 1 shows where such wrapping providers fit in the Entity Framework pipeline.

    Figure 1: Wrapping providers in Entity Framework 4.

    Wrapping 

providers in Entity Framework 4.

    The user code calls into the object context which, in turn, creates an Entity Framework connection. At this time, you can kick in and override the data provider configured in the EDMX script with your own. It has to be anyway an ADO.NET data provider extended with some additional capabilities.

    In doing so, you take advantage of the public provider model which makes it possible for provider writers to support 3rd-party databases, such as Oracle, MySQL, PostgreSQL, and so forth. The provider interface used by Entity Framework is stackable meaning that your provider may intercept communication between Entity Framework and the original provider. In this way, the wrapper provider gets a chance do things like implementing the Cache-Through pattern, logging SQL commands, and more. A working example of wrapping provider that provides caching and tracing capabilities is available at http://code.msdn.microsoft.com/EFProviderWrappers.

    The first step entails registering the wrapping provider with Entity Framework. This is typically done through manual intervention in the configuration file. Here's an example:

    If you want to try this out, just use the sample from the previous link. If you don't like working with configuration, you can use the following fluent code:

    It goes without saying that EFCachingProviderConfiguration is a class in the wrapping provider we're using as an example. Here's an excerpt that shows the source code of method RegisterProvider:

    It should be clear that a wrapping provider is simply the container of extensions. If you want caching, you have to provide it. The aforementioned sample code defines an ICache interface and implements it in an in-memory provider using a dictionary to store data. The next step consists in extending the object context with a reference to the cache provider you plan to use.

    At this point, we're almost done and just need to write the user-level code. Here's a sample console application:

    You create a global instance of the cache provider and attach it to object context. The sample uses three different instances of the object context but only the first time a query hits the database. (See Figure 2.)

    Figure 2: SQL Profiler and the caching provider within EF.

    SQL 

Profiler and the caching provider within EF.

    Refer to the previous link for any code related to sample cache-enabled providers for Entity Framework. You'll find code that uses memory (only useful for demo purposes), the ASP.NET cache, and AppFabric Caching Services.

    Second-level Caching with NHibernate

    The life time of the second level cache is tied to the session factory and not to an individual session. Most of the times, this means the lifetime of the application. Once an entity is loaded by its unique id and the second level cache is active the entity is available for all other sessions obtained through the same session factory. Thus once the entity is in the second level cache NHibernate won't load the entity from the database until it is removed from the cache. You enable and configure the second level cache through the configuration file where, among other things, you choose which cache provider to use. Some cache providers are natively included with NHibernate. You can configure the NHibernate cache in various ways. For example, it can be limited to load a single entity, entity sets via lazy loading, or just any query that returns a collection of entities. For more information on the internals of NHibernate caching, pay a visit to the following blog: http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/11/09/first-and-second-level-caching-in-nhibernate.aspx.

    Second level cache providers you can find useful with NHibernate are not part of the core engine but have been contributed to the tool. Here's a list:

    Table 1: Second level cache providers.

    Cache Provider

    Description

    AppFabric Caching Services

    Caching is based on Microsoft's AppFabric Caching Services which is a distributed in-memory application cache platform.

    Prevalence

    Caching is based on Bamboo.Prevalence . Bamboo.Prevalence develops around the idea that any objects are kept locally in memory, any operation is being logged, and persistence can be configured to happen periodically. Persistence just saves a snapshot of all business objects to a file using plain object serialization.

    SysCache

    Caching is based on System.Web.Caching.Cache just like in ASP.NET.

    SysCache2

    Similar to SysCache, except that it also supports SQL dependencies.

    MemCache

    Caching is based on MemCached. MemCached is a distributed caching system that stores data in memory using a distributed hash table. (By the way, MemCached is the caching system used by sites like Facebook, Twitter, YouTube.)

    SharedCache

    Caching is based on SharedCache, a full .NET distributed caching system.

    In addition, third-party vendors of distributed caching systems have their own provider for NHibernate. This is the case with NCache and ScaleOut.

    To start out, you tell NHibernate which cache provider to use by using the following script in the hibernate.cfg.xml file as follows:

    If the selected cache provider does require its own settings (it will usually do), you are expected to list them all following the specific provider configuration syntax.

    You must be aware that a NHibernate cache provider maintains three lists: cached entities, cached queries, timestamps. Every entity that is retrieved is cached as a sequence of primitive values (associations must be cached separately) and is identified by ID. This means that if you request a specific entity by ID you'll be served a new instance of the entity rehydrated with the stored list of values. This simplifies the internal management of data as the cache is an array of identifiers rather than a graph of objects. For entities to be cached, however, it is essential that they support caching. Here's how you enable caching on individual entities:

    General queries that return a collection of objects can be cached as well. To enable this feature, you define the following setting:

    Not all queries are automatically cached because of this setting. It simply means that the feature is enabled, but only queries that explicitly claim to be cacheable are actually cached. Here's an example:

    The SetCacheable method does the trick.

    The timestamp cache stores the last time a table was written to and is updated only upon the commit of a transaction. The timestamp cache is used internally by the cache provider to avoid serving stale data to users.

    Finally, it is worth noting that caches don't detect any changes made to the database by another process. If this is just what happens in your scenario you might want to expire data regularly. As mentioned, a cache is bound to SessionFactory as far as its lifetime is concerned. Everything is lost as soon as the session factory is gone.

    Summary

    Caching is a vital aspect of enterprise applications. When an enterprise application uses an O/RM tool to persist its data, caching automatically becomes a vital aspect of the O/RM. NHibernate is very aware of this fact and offers an easy mechanism to plug in a cache provider. Things are significantly different for Entity Framework where you just have a generic extensibility point and just a couple of demos to apply caching. In this regard, second-level caching is a point where the clear winner is NHibernate.

    The NHibernate Comparison of Entity Framework series

  • Part 1 A comparison on multiple database support in the Entity Framework and NHibernate O/RMs.
  • Part 2 Last month I took the challenge of trying to compare Entity Framework (EF4) and NHibernate (NH) in a hopefully unbiased and feature-driven way. In this article, I'll start looking into some programming features such as lazy loading.
  • Part 3 This article is about fetch plans-a recognized and common way for developers to instruct the O/RM about the structure of the SQL you desire.
  • Part 4 A Feature-driven Comparison of Entity Framework and NHibernate-2nd Level Caching
  • Part 5 A Feature-driven Comparison of Entity Framework and NHibernate - Self-tracking entities
  • Part 6 A Feature-driven Comparison of Entity Framework and NHibernate - Queries
  • <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    About Dino Esposito

    Dino Esposito is one of the world's authorities on Web technology and software architecture. Dino published an array of books, most of which are considered state-of-the-art in their respective areas. His most recent books are “Microsoft ® .NET: Architecting Applications for the Enterprise” and “...

    This author has published 53 articles on DotNetSlackers. View other articles or the complete profile here.

    Other articles in this category


    A Feature-driven Comparison of Entity Framework and NHibernate-Lazy loading
    Last month I took the challenge of trying to compare Entity Framework (EF4) and NHibernate (NH) in a...
    A Feature-driven Comparison of Entity Framework and NHibernate - Queries
    Let's explore what Entity Framework and NHibernate has to offer when it comes to their query capabil...
    A Feature-driven Comparison of Entity Framework and NHibernate—Fetch Plans
    This article is about fetch plans-a recognized and common way for developers to instruct the O/RM ab...
    A Feature-driven Comparison of Entity Framework and NHibernate - Self-tracking entities
    In this article, Dino Esposito introduces self-tracking entities.
    .NET type generation for NHibernate mapping collections
    An overview of .NET type generation for NHibernate mapping collections.

    You might also be interested in the following related blog posts


    NHibernate : Some Naked Thoughts read more
    Sneak Peek at the EntityDataSource Control read more
    Inheritance and the Entity Framework read more
    Exploring EntityKeys, Web Services and Serialization a little further read more
    DLinq: Playing with knives. read more
    Top
     
     
     

    Please login to rate or to leave a comment.

    Free Agile Project Management Tool from Telerik
    TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.