简体   繁体   English

在WCF中缓存?

[英]Caching in WCF?

I am building a WCF service. 我正在构建一个WCF服务。 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? 我需要将参考数据存储在缓存中,每当我从方法接收输入时我都会查找...这样做的正确方法是什么? I would also like to define an expiration policy for the cache that will invalidate it after a certain time interval. 我还想为缓存定义一个到期策略,该策略将在一定时间间隔后使其失效。

如果您使用的是.NET 4,建议使用MemoryCache

Any caching solution should address two basic problems 任何缓存解决方案都应解决两个基本问题

1) Storage of cache items and retrieval 1)存储缓存项和检索

2) Cache invalidation 2)缓存失效

Since Http caching is a well known one I am not going to explain it in detail. 由于Http缓存是一个众所周知的缓存,我不打算详细解释它。 You can use asp compatibility attribute alone with some web configuration, where you will get caching by charm. 您可以单独使用asp兼容性属性和一些Web配置,您可以通过魅力获得缓存。

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

And the web config is like 而web配置就像

<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. 我使用了Microsoft企业库缓存块来解决我的所有缓存存储需求。 However, you still need to do the plumbing to integrate Microsoft enterprise library caching block with your WCF service. 但是,您仍需要执行管道以将Microsoft企业库缓存块与WCF服务集成。 First you have to intercept the WCF communication channel to implement the cache. 首先,您必须拦截WCF通信通道以实现缓存。 A detail discussion of how to intercept the WCF communication channel can be found at http://msdn.microsoft.com/en-us/magazine/cc163302.aspx . 有关如何拦截WCF通信通道的详细讨论,请访问http://msdn.microsoft.com/en-us/magazine/cc163302.aspx This is how you do the plumbing for WCF caching 这是您如何为WCF缓存执行管道工作

基本的管道架构

Step 0 Let's say you have an operation contract as follows and you want to cache the item return by that method. 步骤0假设您有一个操作合同,如下所示,您希望通过该方法缓存项目返回。

[OperationContract]
MyCompositeClass Rotate(int angle)

Step 1 First you have to register your custom cacher in the WCF pipeline. 步骤1首先,您必须在WCF管道中注册自定义cacher。 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. 为此,我将使用一个属性,以便我可以根据面向方面的编程原则很好地装饰我的WCF调用。

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 第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 第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 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. 您还需要在项目参考中添加相关的dll。

<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>

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. 构建您计划缓存的信息类型的模型,并将其用作构建密钥的模板。

You could take a look at Velocity . 你可以看一下Velocity This is Microsoft's distributed in-memory caching framework . 这是Microsoft的分布式内存缓存框架 But this may be a little bit too beta... 但这可能有点过于测试......

Rather than expiring the cache data every so often, you can actually just make sure to invalidate the cache whenever the underlying data you are caching changes. 您可以实际上确保在缓存的基础数据发生更改时确保使缓存无效,而不是每隔一段时间使缓存数据过期。

See this example from info Q http://www.infoq.com/news/2011/04/Attribute-Caching 请参阅信息Q http://www.infoq.com/news/2011/04/Attribute-Caching中的此示例

[Cache.Cacheable("UserTransactionCache")]
public DataTable GetAllTransactionsForUser(int userId)
{
    return new DataProvider().GetAllTransactionsForUser(userId);
}

[Cache.TriggerInvalidation("UserTransactionCache")]
public void DeleteAllTransactionsForUser(int userId)
{
 ...
}

You can use System.Web.Cache (even if you're not in a web context), and that's what I'd do. 您可以使用System.Web.Cache(即使您不在Web环境中),这就是我要做的。 It's basically a big, in memory hash table with some niceties for expiring contents. 它基本上是一个很大的内存哈希表,有一些内容过期的细节。

There are many ways you can do this. 有很多方法可以做到这一点。 One fairly easy is to host the System.Web.Cache object yourself and use that to store the reference data. 一个相当容易的是自己托管System.Web.Cache对象并使用它来存储引用数据。 There's a good example of that here: http://kjellsj.blogspot.com/2007/11/wcf-caching-claims-using.html 这里有一个很好的例子: http//kjellsj.blogspot.com/2007/11/wcf-caching-claims-using.html

The WCF REST Starter Kit has caching, here is an article about using it... with sample code. WCF REST入门套件有缓存,这是一篇关于使用它的文章......带有示例代码。

http://weblogs.asp.net/gsusx/archive/2008/10/29/adding-caching-to-wcf-restful-services-using-the-rest-starter-kit.aspx http://weblogs.asp.net/gsusx/archive/2008/10/29/adding-caching-to-wcf-restful-services-using-the-rest-starter-kit.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM