[WCF REST] 通过ASP.NET Output Caching实现声明式缓存

简介:

ASP.NET的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行缓存。对于后续针对相同资源的请求,只需要直接将缓存的HTML予以回复而无须按照页面处理生命周期对每次请求进行重复处理。WCF通过操作行为AspNetCacheProfileAttribute利用ASP.NET的输出缓存提供一种针对于某个操作的声明式缓存机制。[源代码从这里下载]

一、AspNetCacheProfileAttribute

WCF对ASP.NET缓存的支持是通过AspNetCacheProfileAttribute特性来实现的。通过如下的代码我们不难看出AspNetCacheProfileAttribute是实现了IOperationBehavior接口的操作行为,我们可以直接将其应用到契约接口/类中的某个具有缓存需要的操作方法上。

   1: [AttributeUsage(AttributeTargets.Method)]
   2: public sealed class AspNetCacheProfileAttribute : Attribute, IOperationBehavior
   3: {
   4:     //其他成员
   5:     public AspNetCacheProfileAttribute(string cacheProfileName);
   6:     public string CacheProfileName { get; }
   7: }

AspNetCacheProfileAttribute构造函数参数cacheProfileName表示的CacheProfile的配置名称,目标操作按照定义在相应CacheProfile的缓存策略实施缓存。CacheProfile配置在<system.web>/<caching>/<outputCacheSettings>/<outputCacheProfiles>节点下。

   1: <configuration>
   2:   <connectionStrings>
   3:     <add name="localDb" 
   4:          connectionString="Server=.; Database=TestDb; Uid=sa; Pwd=password"
   5:          providerName="System.Data.SqlClient"/>
   6:   </connectionStrings>
   7:   <system.web>
   8:     <caching>
   9:       <outputCacheSettings>
  10:         <outputCacheProfiles>
  11:           <add name="default" 
  12:                duration="60" 
  13:                varyByParam="none" 
  14:                sqlDependency="TestDb: TestTable"/>
  15:         </outputCacheProfiles>
  16:       </outputCacheSettings>
  17:       <sqlCacheDependency>
  18:         <databases>
  19:           <add name="TestDb" connectionStringName="localDb"/>
  20:         </databases>
  21:       </sqlCacheDependency>
  22:     </caching>
  23:   </system.web>
  24: </configuration>

在如上所示的配置片断中,我们定义了一个名称为default的CacheProfile。代表缓存时间的duration属性被设置为60,意味着缓存项在被存储之后1分钟之后实失效;属性varyByParam被设置为none表示缓存项与请求的查询字符串无关。此外,该CacheProfile还设置针对某个本地数据库中的TestTable表的SQL依赖(SQL Dependency)。关于CacheProfile的配置属于ASP.NET的范畴,在这里我们不会作过多的讨论。

既然是采用ASP.NET输出缓存,WCF服务自然需要采用IIS寄宿并采用ASP.NET 兼容模式。值得一提的是,基于AspNetCacheProfileAttribute的输出缓存仅仅针对HTTP-GET

二、实例演示:创建采用输出缓存的服务

接下来我们通过一个简单的实例来演示如何通过操作行为对某个操作的返回值实施缓存,为此我们创建一个用于返回当前时间的服务。如下所示的是作为服务契约的ITime接口的定义,AspNetCacheProfileAttribute特性被应用到了用于返回当前时间的操作方法GetCurrentTime上。

   1: using System;
   2: using System.ServiceModel;
   3: using System.ServiceModel.Web;
   4: namespace Artech.WcfServices.Service.Interface
   5: {
   6:     [ServiceContract(Namespace = "http://www.artech.com/")]
   7:     public interface ITime
   8:     {
   9:         [WebGet(UriTemplate = "/current")]
  10:         [AspNetCacheProfile("default")]
  11:         DateTime GetCurrentTime();
  12:     }
  13: }

实现了契约接口ITime的服务类型TimeService定义如下。我们将AspNetCompatibilityRequirementsAttribute特性应用在服务类型上并将RequirementsMode属性设置为Allowed以提供对ASP.NET兼容模式的支持。

   1: using System;
   2: using System.ServiceModel.Activation;
   3: using Artech.WcfServices.Service.Interface;
   4: namespace Artech.WcfServices.Service
   5: {
   6:     [AspNetCompatibilityRequirements(
   7:      RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   8:     public class TimeService : ITime
   9:     {
  10:         public DateTime GetCurrentTime()
  11:         {
  12:             return DateTime.Now;
  13:         }
  14:     }
  15: }

在一个Web项目中我们为通过IIS寄宿的服务TimeService添加一个对应的.svc文件(TimeService.svc),如下所示的是<%@ServiceHost%>指令的定义。表示ServiceHostFactory类型的指令属性Factory被设置为System.ServiceModel.Activation.WebServiceHostFactory.

   1: <%@ ServiceHost Service="Artech.WcfServices.Service.TimeService" Factory="System.ServiceModel.Activation.WebServiceHostFactory"%>

我们在作为服务宿主的Web项目下添加一个配置文件(Web.config)并定义如下的配置。除了服务寄宿的基本配置外,我们将<system.serviceModel>/<serviceHostingEnvironment >配置节的aspNetCompatibilityEnabled属性设置为True以开启ASP.NET兼容模式。应用在操作方法GetCurrentTime上的AspNetCacheProfileAttribute特性中指定的名称为default的CacheProfile定义在该配置中,duration和varyByParam分别被设置为60和none。

   1: <configuration>
   2:     <system.serviceModel>
   3:         <services>
   4:             <service name="Artech.WcfServices.Service.TimeService">
   5:                 <endpoint binding="webHttpBinding"
   6:                           contract="Artech.WcfServices.Service.Interface.ITime"/>
   7:             </service>
   8:         </services>
   9:       <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  10:     </system.serviceModel>  
  11:   <system.web>
  12:     <caching>
  13:       <outputCacheSettings>
  14:         <outputCacheProfiles>
  15:           <add name="default" duration="60" varyByParam="none"/>
  16:         </outputCacheProfiles>
  17:       </outputCacheSettings>      
  18:     </caching>
  19:   </system.web>
  20: </configuration>

作为客户端的控制台程序在进行了相应配置之后通过如下的代码进行服务的调用。在这段代码中,我们通过创建的服务代理进行了5次服务调用,并将获取的时间打印出来。每次服务的时间间隔为1秒。

   1: using (ChannelFactory<ITime> channelFactory =  new ChannelFactory<ITime>("timeService"))
   2: {
   3:     ITime proxy = channelFactory.CreateChannel();
   4:     for (int i = 0; i < 5; i++)
   5:     { 
   6:         Console.WriteLine(proxy.GetCurrentTime().ToLongTimeString());
   7:         Thread.Sleep(1000);
   8:     }
   9: }

客户端代码执行之后会在控制台上输出如下的结果。由于服务端通过ASP.NET的输出缓存对第一次执行GetCurrentTime操作的结果进行了缓存,所以客户端返回的时间都是相同的。

   1: 4:48:43 PM
   2: 4:48:43 PM
   3: 4:48:43 PM
   4: 4:48:43 PM
   5: 4:48:43 PM

三、 AspNetCacheProfileAttribute是如何实现输出缓存的?

既然我们采用ASP.NET兼容模式来寄宿服务,意味着我们调用某个服务与访问某个页面没有本质的区别,所以基于Web页面的输出缓存能够应用于基于某个服务操作的调用就不足为奇了。现在有这么一个问题:通过AspNetCacheProfileAttribute特性指定CacheProfile是如何生效的?

如果对ASP.NET具有一定的了解,应该知道可以通过当前HttpResponse(HttpContext.Current.Response)的Cache属性表示的HttpCachePolicy对象来控制当前输出缓存的基本策略。实际上AspNetCacheProfileAttribute就是通过这种方式将定义在指定CacheProfile的缓存策略应用到针对当前操作的调用上的。

具体来说,AspNetCacheProfileAttribute针对输出缓存策略的控制是通过一个实现了接口IParameterInspector的自定义参数检验器实现的,这是一个名称为CachingParameterInspector的内部类型。操作行为AspNetCacheProfileAttribute通过实现的ApplyDispatchBehavior方法将针对某个CacheProfile创建的CachingParameterInspector对象添加到当前分发操作(DispatchOperation)的参数检验器列表中。

   1: internal class CachingParameterInspector : IParameterInspector
   2: {
   3:     public CachingParameterInspector(string cacheProfileName);
   4:     public object BeforeCall(string operationName, object[] inputshens);
   5:     public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
   6:     {
   7:         //将指定CacheProfile的输出缓存策略应用到当前HttpResponse
   8:     }
   9: }

如上面的代码片断所示,当AfterCall方法被执行的之后,在构造函数中指定的CacheProfile定义的输出缓存策略应用到当前HttpResponse。而AfterCall会在操作执行之后,回复消息序列化之前被执行。


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
缓存 数据格式
实现LRU缓存的三种方式(建议收藏)
LRU全称为Least Recently Used,即最近使用的。针对的是在有限的内存空间内,只缓存最近使用的数据(即get和set的数据),超过有限内存空间的数据将会被删除。这个在面试题中也是常会被问到的内容,接下来就看看怎么来实现。
946 0
实现LRU缓存的三种方式(建议收藏)
|
6月前
|
缓存 开发框架 .NET
ASP.NET Core 缓存
Get新知识: 缓存相关概念:缓存的类型:总结:总的来说,私有缓存会减少网络带宽的需求,同时会减少从缓存到API的请求。因为私有缓存是存储在客户端浏览器的,对于请求来说,如果缓存还在有限期内,那么请求连网络请求都不会发出会直接在客户端浏览器获取到响应,这样就减少网络请求次数,同样也会减少API请求次数。而共享缓存不会节省缓存到API的网路带宽,但是它会减少请求到API的请求。因为共享缓存是...
32 0
ASP.NET Core 缓存
|
3月前
|
存储 缓存 PHP
|
9月前
|
存储 SQL 缓存
ASP.NET Core MVC 从入门到精通之缓存
ASP.NET Core MVC 从入门到精通之缓存
85 0
|
12月前
|
存储 缓存 开发框架
Asp.Net Core 2.1+的视图缓存(响应缓存)
Asp.Net Core 2.1+的视图缓存(响应缓存)
68 0
|
缓存 PHP
在Yii2.0框架中,可以使用yii\caching\TagDependency类来实现对缓存的依赖管理,这个类可以在缓存失效时自动清除缓存。使用前需要先use吗?
在Yii2.0框架中,可以使用yii\caching\TagDependency类来实现对缓存的依赖管理,这个类可以在缓存失效时自动清除缓存。使用前需要先use吗?
|
机器学习/深度学习 缓存 Oracle
【数据库设计与实现】第7章:缓存与检查点
缓存与检查点设计原则数据缓冲区与检查点是相辅相成的,所以放在同一个章节介绍。由于CPU与持久化设备之间存在巨大的速度差距,所以在内存中引入缓冲区缩小这个差距。从读的角度来看,将热点数据或预判用户可能读取的数据提前加载到内存中,从而将持久化设备的读时延和带宽提升至内存的时延和带宽。从写的角度来看,直接修改缓冲区中的数据而不是磁盘中的数据,可以带来两方面的优势。其一,将持久化设备的写时延和带宽提升至内
【数据库设计与实现】第7章:缓存与检查点
|
缓存 JSON Java
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
335 1
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
|
存储 缓存 算法
基于LinkedHashMap实现LRU缓存
基于LinkedHashMap实现LRU缓存
167 0
基于LinkedHashMap实现LRU缓存
|
缓存 算法 安全
如何使用 LinkedHashMap 实现 LRU 缓存?
在上一篇文章里,我们聊到了 HashMap 的实现原理和源码分析,在源码分析的过程中,我们发现一些 LinkedHashMap 相关的源码,当时没有展开,现在它来了。 那么,LinkedHashMap 与 HashMap 有什么区别呢?其实,LinkedHashMap 的使用场景非常明确 —— LRU 缓存。今天,我们就来讨论 LinkedHashMap 是如何实现 LRU 缓存的。
135 0