在WCFcaching?

我正在构build一个WCF服务。 我需要将参考数据存储在caching中,每当我从方法接收input时,我都会查找参考数据。什么是正确的方法来做到这一点? 我还想定义caching的过期策略,在一定的时间间隔后将使其无效。

如果您使用.NET 4,推荐的方法是使用MemoryCache

任何caching解决scheme都应该解决两个基本问题

1)存储caching项目和检索

2)caching失效

由于Httpcaching是一个众所周知的,我不会详细解释它。 您可以单独使用asp兼容性属性进行一些网页configuration,在这里您将通过魅力进行caching。

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

而networkingconfiguration就像

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

但是这并不适用于大多数场景,尤其是当您有大型复杂的对象caching时。 例如,我有一种情况,我想要caching一个系统生成的图像(操作合同的输出是一个系统生成的图像,取决于input)。 在这种情况下,你必须实现你自己的caching。 我已经使用了Microsoft企业库caching块来解决我的所有caching存储需求。 但是,您仍然需要完成将Microsoft企业库caching块与WCF服务集成在一起的工作。 首先你必须拦截WCF通信通道来实现caching。 有关如何拦截WCF通信频道的详细讨论,请访问http://msdn.microsoft.com/en-us/magazine/cc163302.aspx 。 这是你如何做的WCFcachingpipe道

基本的管道结构

第0步假设你有一个如下的操作合同,你想通过该方法caching项目返回。

 [OperationContract] MyCompositeClass Rotate(int angle) 

步骤1首先,您必须在WCFpipe道中注册自定义的cacher。 要做到这一点,我要使用一个属性,以便我可以很好地装饰我的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; } } 

第2步

然后你必须实现caching对象将被检索的点。

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

第3步

最后在您的服务电话上添加您的属性

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

企业库caching块的configuration超出了这个答案的范围。 你可以使用下面的链接来学习它。 企业图书馆的好处是你已经做好了扩展你的caching策略的方法。 它内置了caching过期和存储的方式。 您也可以编写自己的caching过期和存储策略。 http://www.codeproject.com/KB/web-cache/CachingApplicationBlock.aspx

最后一件事,为了让您的企业库caching工作,您需要添加以下configuration详细信息。 您还需要将相关的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> 

你可以看看速度 。 这是微软的分布式内存caching框架 。 但是这可能有点太…

这是一个很好的文章: http : //cascadeofinsights.com/post/1410736927/introducing-attribute-based-caching

如果您要扩展到负载均衡,无状态系统中的多个服务器,则需要devise使用分布式caching 。 这里要做的主要事情是:

  1. 同时使用本地和分布式caching。 只将会话或短期的东西放在分布式caching中,其他东西在本地caching。

  2. 为项目设置适当的超时。 这取决于信息的types以及它需要的来源有多接近。

  3. 当你知道这将是失禁(如更新,删除等),从caching中删除的东西。

  4. 注意devise唯一的caching键。 构build您计划caching的信息types的模型,并将其用作构build密钥的模板。

你可以使用System.Web.Cache(即使你不在web上下文中),这就是我所要做的。 它基本上是一个很大的内存哈希表,有一些内容过期的好处。

有很多方法可以做到这一点。 一个相当简单的方法是自己托pipeSystem.Web.Cache对象,并使用它来存储参考数据。 这里有一个很好的例子: http : //kjellsj.blogspot.com/2007/11/wcf-caching-claims-using.html

WCF REST入门工具包有caching,这里是一个关于使用它的文章…与示例代码。

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

不要每隔一段时间过期caching数据,只要caching中的基础数据发生变化,就可以确保使caching失效。

从info 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) { ... }