Caching in WCF?

前端 未结 9 961
故里飘歌
故里飘歌 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条回答
  •  猫巷女王i
    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

    
                
    
    
       
          
             
                
             
          
       
    
    

    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;
            }
    
            /// 
            /// Decorate the method call with the cacher
            /// 
            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.

    
        

提交回复
热议问题