Search code examples
appfabric

AppFabric Cache standalone mode?


As an ISV I'd like to be able to program my middle tier using the AppFabric Caching Service, but then be able to deploy in small (single server) environments without the need to have AppFabric Cache Server(s) deployed. It also seems natural to me that a "in-memory only" version of the cache client would be ideal for standalone development.

However, all the research I've done so far implies that I have to load a real cache server to make some of the apis work at all, and that the current "Local" option does not fit the bill for what I want.

It seems to me that what I'm looking for would work similarly to aspx session cache, in that the out of the box mechanism is in-memory, and then you can choose to configure the older external process provider, or the sql provider, and now the AppFabric provider, giving better and better scalability as you move up. This works great for aspx session.

Am I correct in thinking that there is no equivalent solution for programming and deploying in a "small" environment for AppFabric caching?


Solution

  • There's a number of issues raised in this question, let's see if we can tackle them...

    First and foremost, as Frode correctly points out you can run an AppFabric instance quite happily on one server - it's what I do most of the time for playing around with the API. Obviously the High Availability feature isn't going to be, well, available, but from the question itself I think you've already accepted that.

    Secondly, you can't use the AppFabric API against the Local cache - the local cache is only there to save an AppFabric client trips across the wire to a dedicated AppFabric cache server.

    Now, to configurable caches, which I think is the most interesting part. What I think you want to do here is separate the operations on the cache from the cache itself into a generic interface, and then you write your code against the interface at design time, and at runtime you create a cache based on information from your app.config/web.config.

    So let's start by defining our interface:

    public interface IGenericCache
    {
        void Add(string key, object value);
        void Remove(string key);
        Object Get(string key);
        void Update(string key, object value);
    }
    

    And now we can define a couple of implementations, one using the MemoryCache and one using AppFabric.

    using System.Runtime.Caching;
    
    class GenericMemoryCache : IGenericCache
    {
        public void Add(string key, object value)
        {
            MemoryCache cache = new MemoryCache("GenericMemoryCache");
    
            cache.Add(key, value, null, null);
        }
    
        public void Remove(string key)
        {
            MemoryCache cache = new MemoryCache("GenericMemoryCache");
    
            cache.Remove(key, null);
        }
    
        public object Get(string key)
        {
            MemoryCache cache = new MemoryCache("GenericMemoryCache");
    
            return cache.Get(key, null);
        }
    
        public void Update(string key, object value)
        {
            MemoryCache cache = new MemoryCache("GenericMemoryCache");
    
            cache.Set(key, value, null, null);
        }
    }
    
    using Microsoft.ApplicationServer.Caching;
    
    class GenericAppFabricCache : IGenericCache
    {
        private DataCacheFactory factory;
        private DataCache cache;
    
        public GenericAppFabricCache()
        {
            factory = new DataCacheFactory();
            cache = factory.GetCache("GenericAppFabricCache");
        }
    
        public void Add(string key, object value)
        {
            cache.Add(key, value);
        }
    
        public void Remove(string key)
        {
            cache.Remove(key);
        }
    
        public object Get(string key)
        {
            return cache.Get(key);
        }
    
        public void Update(string key, object value)
        {
            cache.Put(key, value);
        }
    }
    

    And we could go on and write IGenericCache implementations with the ASP.NET Cache, NCache, memcached...

    Now we add a factory class that uses reflection to create an instance of one of these caches based on values from the app.config/web.config.

    class CacheFactory
    {
        private static IGenericCache cache;
    
        public static IGenericCache GetCache()
        {
            if (cache == null)
            {
                // Read the assembly and class names from the config file
                string assemblyName = ConfigurationManager.AppSettings["CacheAssemblyName"];
                string className = ConfigurationManager.AppSettings["CacheClassName"];
    
                // Load the assembly, and then instantiate the implementation of IGenericCache
                Assembly assembly = Assembly.LoadFrom(assemblyName);
                cache = (IGenericCache) assembly.CreateInstance(className);
            }
            return cache;
        }
    }
    

    Anywhere the client code needs to use the cache, all that is needed is a call to CacheFactory.GetCache, and the cache specified in the config file will be returned, but the client doesn't need to know which cache it is because the client code is all written against the interface. Which means that you can scale out your caching simply by changing the settings in the config file.

    Essentially what we're written here is a plugin model for caching, but be aware that you're trading off flexibility for features. The interface has to be more or less the lowest common denominator - you lose the ability to use, say, AppFabric's concurrency models, or the tagging API.

    There's an excellent and more complete discussion of programming against interfaces in this article.