[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会在操作执行之后,回复消息序列化之前被执行。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个爱瞎折腾的程序猿

在asp.net core2.1中添加中间件以扩展Swashbuckle.AspNetCore3.0支持简单的文档访问权限控制

在此之前的接口项目中,若使用了 Swashbuckle.AspNetCore,都是控制其只在开发环境使用,不会就这样将其发布到生产环境(安全第一) 。 那么,...

1551
来自专栏哲学驱动设计

数据层扩展包EFCachingProvider 总结

    前不久学习了《EFCachingProvider》,该扩展包不但可以用于EntityFramework的扩展,所有与数据库连接相关的应用程序都可以使用类...

1837
来自专栏大内老A

深入剖析ASP.NET的编译原理之一:动态编译(Dynamical Compilation)

Microsoft 的Visual Studio为我们在应用开发中提供的强大功能,我们是有目共睹。借助该工具,是我们的开发 显得更加高效而轻松。从Microso...

1965
来自专栏恰同学骚年

设计模式的征途—19.命令(Command)模式

在生活中,我们装修新房的最后几道工序之一是安装插座和开关,通过开关可以控制一些电器的打开和关闭,例如电灯或换气扇。在购买开关时,用户并不知道它将来到底用于控制什...

552
来自专栏博客园

深入浅出话资源

我们把有用的东西称为资源。“兵马未动,粮草先行”-----程序中的各种数据就是算法的原料和粮草。程序中可以存放数据的地方有很多,可以放在数据库里、可以存储在变量...

962
来自专栏大内老A

[WCF权限控制]利用WCF自定义授权模式提供当前Principal[实例篇]

在《原理篇》中我们谈到:如果采用自定义安全主体权限模式,我们可以通过自定义AuthorizationPolicy或者ServiceAuthorizationMa...

25610
来自专栏大内老A

回调与并发: 通过实例剖析WCF基于ConcurrencyMode.Reentrant模式下的并发控制机制

对于正常的服务调用,从客户端发送到服务端的请求消息最终会被WCF服务运行时分发到相应的封装了服务实例的InstanceContext上。而在回调场景中,我们同样...

1867
来自专栏大内老A

深入剖析ASP.NET的编译原理之一:动态编译(Dynamical Compilation)

Microsoft 的Visual Studio为我们在应用开发中提供的强大功能,我们是有目共睹。借助该工具,是我们的开发 显得更加高效而轻松。从Microso...

20110
来自专栏圣杰的专栏

事件总线知多少(2)

源码路径:Github-EventBus 事件总线知多少(1) 事件总线知多少(2) 1.引言 之前的一篇文章事件总线知多少(1),介绍了什么是事件总线...

2307
来自专栏张善友的专栏

ASP.NET MVC的Action Filter

一年前写了一篇短文ASP.NET MVC Action Filters,整理了Action Filter方面的资源,本篇文章详细的描述Action Filter...

22210

扫码关注云+社区