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.

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.

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
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.
|
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
|
|
Please login to rate or to leave a comment.