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

Application Design for Cross Cutting Concerns (like Caching, Logging etc..) using DI Framework

4.50/5 (6 votes)
27 Sep 2010CPOL4 min read 28.1K   393  
Ability to plugin the application's cross cutting concerns using Dependency Injection framework

Introduction

In today's world, application design should account for the ability to plug in the cross cutting concerns (like caching, logging and exception handling, etc.), to be able to scale out the application for future requirements without incurring additional maintenance costs.

Background

I was working on a client project recently and was tasked to take a look at performance tuning of the application and provide recommendations for scaling out the application. As part of the process, one of the things we have observed is that the specific implementation of cross-cutting concern (to cite here, Caching) is so tightly integrated into the business layer (usage HttpContext.Current.Cache) thus preventing (if not preventing, making it harder requiring huge code change, testing effort, delayed timelines and last but not least re-deployment) to scale out the application.

The developers and designers have used ASP.NET cache (not that ASP.NET cache is bad rather it is not global / distributed cache), thus making the cache object to be duplicated on each web server in the farm. In this case, populating the data into cache is costly as the query that needs to be run is complex (infact very complex) and takes few seconds to execute. This has triggered me to write up an article that would help people out there on how they can create pluggable design.

I would like to thank Mr Anil Sistla (Architect from Virtusa) for the architectural idea and for continuous support in the solution implementation.

This solution puts forth how a IoC (Inversion of Control Container) can help you to get around these kind of dependencies and shows how you could have been able to scale out easily by switching the caching data provider layer with minimal configuration change.

In this sample, the IoC container I have used is StructureMap. There are other IoC frameworks out there, evaluate and use one based on your requirement. This topic is not about specific Ioc, rather how you can use IoC to provide a pluggable design, and it is not just limited to addressing cross cutting concerns, rather the concept can be applied to any part of the application.

This sample uses Memcached Server, MemcachedClient and ASP.NET cache.

Using the Code

Below is the code structure for the cache sample:

CodeStructure.jpg

We have an interface named CacheProviders.Interfaces.ICache. For demo purposes, this interface provides three basic operations SetItem, GetItem and RemoveItem.

We have two implementations of this ICache interface, one named CacheDataProviders.Memcached and the other named CacheDataProviders.AspNet.

AspNetCacheProvider implementation of the Icache interface:

C#
public class AspNetCacheProvider : Providers.ICache {

       public AspNetCacheProvider() {
       }

       bool Providers.ICache.SetItem(string key, object value) {
           HttpRuntime.Cache.Insert(
               key,
               value,
               null,
               System.Web.Caching.Cache.NoAbsoluteExpiration,
               System.Web.Caching.Cache.NoSlidingExpiration,
               System.Web.Caching.CacheItemPriority.Default,
               null);
           return true;
       }

       object Providers.ICache.GetItem(string key) {
           object _item = HttpRuntime.Cache.Get(key);
           return _item;
       }

       bool Providers.ICache.RemoveItem(string key) {
           HttpRuntime.Cache.Remove(key);
           return true;
       }
   }

MemCacheProvider implementation of the Icache interface:

C#
public class MemCacheProvider : Providers.ICache {

       private readonly MemcachedClient _cacheProviderClient;

       // As of this writing, MemCacheClient configuration is done in the app.config
       // of the host / consuming application. Please read the config file as it is
       // self explanatory.
       public MemCacheProvider() {
           _cacheProviderClient = new MemcachedClient();
       }

       bool Providers.ICache.SetItem(string key, object value) {
           bool _stored = _cacheProviderClient.Store(StoreMode.Set, key, value);
           return _stored;
       }

       object Providers.ICache.GetItem(string key) {
           object _value = _cacheProviderClient.Get(key);
           return _value;
       }

       bool Providers.ICache.RemoveItem(string key) {
           bool _removed = _cacheProviderClient.Remove(key);
           return _removed;
       }
   }

CacheDataProviders.MemCached uses eniym.memcached client to store data into memcached.

CacheDataProviders.AspNet uses ASP.NET cache to store data.

CachingSample application is the program that is consuming caching layer without knowing specific details of the CacheDataProvider.
So, for development purposes, you might like to use AspNet cache and for staging / production deployment, you might want to use distributed cache like Memcache or may be your own cache provider (of course, make sure the respective CacheDataProvider is tested).

We use the DI framework to create the necessary cache provider based on the environment (staging or production), this article uses StructureMap DI framework as it is lightweight and easy to demonstrate (There are other DI frameworks available in the market for .NET like Spring.Net, Microsoft's unity, etc. Each has its own merits and demerits, it is upto the developer/designer to choose the appropriate DI framework based on their requirements).

Below is the configuration part of the StructureMap in App.config file, currently it is set to MemCacheProvider. We can flip the plugin from MemCached to ASP.NET easily in the configuration file, and the application will start adopting to the new cache provider in this case ASP.NET cache. If the current cache provider has any limitations, then we can easily scale out the application to a different cache provider by just changing the one step in configuration.

XML
<StructureMap>

   <PluginFamily Type="CacheProviders.Interfaces.ICache"
               Assembly="CacheProviders.Interfaces"
               DefaultKey="DefaultCache">

     <!--
     TODO: Instead of MemCachedProvider, write an abstract base class
     and provide the instance as default cache.
     -->

     <Plugin Assembly="CacheProviders.MemCached"
             Type="CacheProviders.Memcached.MemCacheProvider"
             Scope="Singleton"
             ConcreteKey="DefaultCache" />

     <!--<Plugin Assembly="CacheProviders.AspNet"
             Type="CacheProviders.AspNet.AspNetCacheProvider"
             Scope="Singleton"
             ConcreteKey="DefaultCache" />-->
   </PluginFamily>

 </StructureMap>

Disclaimer

Sample code provided in the article is only for demo purposes, and is not ready for production/deployment purposes. It is the responsibility of the reader to evaluate the risk before integrating the code with his/her application.

License

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