Caching in WCF?

前端 未结 9 917
故里飘歌
故里飘歌 2020-11-29 01:50

I am building a WCF service. I need to store reference data in the cache which I will look up every time I receive input from the method... What is the right way to do this?

相关标签:
9条回答
  • 2020-11-29 02:26

    If you're going to be scaling out to more than one server in a load balanced, stateless system, you'll want to design for use of a distributed cache. The main things to do here are:

    1. Use both a local and distributed cache. Only put session or short lived stuff in the distributed cache, other stuff cache locally.

    2. Set appropriate timeouts for items. This will vary depending on the type of information and how close to the source it needs to be.

    3. Remove stuff from cache when you know it will be incontinent (like updates, deletes, etc).

    4. Take care to design cache keys that are unique. Build a model of the type of information you plan to cache and use that as a template for building keys.

    0 讨论(0)
  • 2020-11-29 02:32

    You can use System.Web.Cache (even if you're not in a web context), and that's what I'd do. It's basically a big, in memory hash table with some niceties for expiring contents.

    0 讨论(0)
  • 2020-11-29 02:38

    Any caching solution should address two basic problems

    1) Storage of cache items and retrieval

    2) Cache invalidation

    Since Http caching is a well known one I am not going to explain it in detail. You can use asp compatibility attribute alone with some web configuration, where you will get caching by charm.

    [AspNetCacheProfile("MyProfile")]
            public Customer GetName(string id)
            {
                 // ...
            }
    

    And the web config is like

    <system.serviceModel>
            <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />    
    </system.serviceModel>
    <system.web>
       <caching>
          <outputCacheSettings>
             <outputCacheProfiles>
                <add name=" MyProfile" duration="600" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
             </outputCacheProfiles>
          </outputCacheSettings>
       </caching>
    </system.web>
    

    But this is not suitable for most scenarios especially when you have large complex object to cache. For example I had a situation where I wanted to cache a system generated image (the output of the operation contract is a system generated image that depends on the input). In such a case, you have to implement your own cache. I have used Microsoft enterprise library caching blocks that addressed all my caching storage requirements. However, you still need to do the plumbing to integrate Microsoft enterprise library caching block with your WCF service. First you have to intercept the WCF communication channel to implement the cache. A detail discussion of how to intercept the WCF communication channel can be found at http://msdn.microsoft.com/en-us/magazine/cc163302.aspx. This is how you do the plumbing for WCF caching

    Basic Plumbing Architecture

    Step 0 Let’s say you have an operation contract as follows and you want to cache the item return by that method.

    [OperationContract]
    MyCompositeClass Rotate(int angle)
    

    Step 1 First you have to register your custom cacher in the WCF pipeline. To do that I am going to use an attribute so that I can nicely decorate my WCF call according to aspect orient programming principles.

    using System;
    using System.ServiceModel.Description;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Dispatcher;
    using System.Reflection;
    
        [AttributeUsage(AttributeTargets.Method)]
        public class MyCacheRegister : Attribute, IOperationBehavior
        {
            ConstructorInfo _chacherImplementation;
            public ImageCache(Type provider)
            {
                if (provider == null)
                {
                    throw new ArgumentNullException("Provider can't be null");
                }
                else if (provider.IsAssignableFrom(typeof(IOperationInvoker)))
                {
                    throw new ArgumentException("The type " + provider.AssemblyQualifiedName + " does not implements the interface " + typeof(IOperationInvoker).AssemblyQualifiedName);
                }
                else
                {
                    try
                    {
                        Type[] constructorSignatureTypes = new Type[1];
                        constructorSignatureTypes[0] = typeof(IOperationInvoker);
                        _chacherImplementation = provider.GetConstructor(constructorSignatureTypes);
    
                    }
                    catch
                    {
                        throw new ArgumentException("There is no constructor in " + provider.AssemblyQualifiedName + " that accept " + typeof(IOperationInvoker).AssemblyQualifiedName + " as a parameter");
                    }
    
                }
    
    
            }
    
            public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
            {
                return;
            }
    
            public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
            {
                return;
            }
    
            /// <summary>
            /// Decorate the method call with the cacher
            /// </summary>
            public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
            {
                //decorator pattern, decorate with a  cacher
                object[] constructorParam = new object[1];
                constructorParam[0] = dispatchOperation.Invoker;
                dispatchOperation.Invoker = (IOperationInvoker)_chacherImplementation.Invoke(constructorParam);
            }
    
            public void Validate(OperationDescription operationDescription)
            {
                return;
            }
        }
    

    Step 2

    Then you have to implement the point where cache object will be retrieved.

    using System;
    using System.ServiceModel.Dispatcher;
    using Microsoft.Practices.EnterpriseLibrary.Caching;
    using Microsoft.Practices.EnterpriseLibrary.Common;
    using System.IO;
    
        class RotateCacher : IOperationInvoker
        {
    
            private IOperationInvoker _innerOperationInvoker;
            public RotateImageCacher(IOperationInvoker innerInvoker)
            {
                _innerOperationInvoker = innerInvoker;
            }
            public object[] AllocateInputs()
            {
                Object[] result = _innerOperationInvoker.AllocateInputs();
                return result;
            }
    
            public object Invoke(object instance, object[] inputs, out object[] outputs)
            {
                object result=null;
    
    ///TODO: You will have more object in the input if you have more ///parameters in your method
    
                string angle = inputs[1].ToString();
    
                ///TODO: create a unique key from the inputs
                string key = angle;
    
                string provider = System.Configuration.ConfigurationManager.AppSettings["CacheProviderName"];
                ///Important Provider will be DiskCache or MemoryCache for the moment
    provider =”DiskCache”;
    ///TODO: call enterprise library cache manager, You can have your own 
    /// custom cache like Hashtable
    
        ICacheManager manager = CacheFactory.GetCacheManager(provider);
    
                if (manager.Contains(key))
                {
    
                    result =(MyCompositeClass) manager[key];
    
                }
                else
                {
                    result =(MyCompositeClass) _innerOperationInvoker.Invoke(instance, inputs, out outputs);
                    manager.Add(key, result);
                }
                return result;
            }
    
            public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
            {
                IAsyncResult result = _innerOperationInvoker.InvokeBegin(instance, inputs, callback, state);
                return result;
            }
    
            public object InvokeEnd(object instance, out object[] outputs, IAsyncResult asyncResult)
            {
                object result = _innerOperationInvoker.InvokeEnd(instance, out outputs, asyncResult);
                return result;
            }
    
            public bool IsSynchronous
            {
                get { return _innerOperationInvoker.IsSynchronous; }
            }
        }
    

    Step 3

    Finally add your attribute above your service call

    [OperationContract]
    [MyCacheRegister(typeof(RotateCacher)]
    MyCompositeClass Rotate(int angle)
    

    The configuration of enterprise library caching block is beyond the scope of this answer. You can use following link to learn it. The good thing about enterprise library is that you get ready made ways to extend your caching policy. It has built in ways for cache expiry and storage. You also can write your own cache expiration and storage policies. http://www.codeproject.com/KB/web-cache/CachingApplicationBlock.aspx

    One final thing, for you to get your enterprise library caching working you need to add following configuration details. You also need to add relevant dlls to your project reference.

    <configSections>
        <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
      </configSections>
    
    
      <cachingConfiguration defaultCacheManager="Cache Manager">
        <cacheManagers>
          <add name="MemoryCache" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000"
            numberToRemoveWhenScavenging="10" backingStoreName="NullBackingStore" />
          <add name="DiskCache" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000"
            numberToRemoveWhenScavenging="10" backingStoreName="IsolatedStorageCacheStore" />
        </cacheManagers>
        <backingStores>
          <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            name="NullBackingStore" />
          <add name="IsolatedStorageCacheStore" type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.IsolatedStorageBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            encryptionProviderName="" partitionName="MyCachePartition" />
        </backingStores>
      </cachingConfiguration>
    
    0 讨论(0)
提交回复
热议问题