找回密码
 立即注册
首页 业界区 业界 [原创]WCF技术剖析之五:利用ASP.NET兼容模式创建支持会 ...

[原创]WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务

锟及 2025-5-29 20:22:26
在《基于IIS的WCF服务寄宿(Hosting)实现揭秘》中,我们谈到在采用基于IIS(或者说基于ASP.NET)的WCF服务寄宿中,具有两种截然不同的运行模式:ASP.NET并行(Side by Side)模式和ASP.NET兼容模式。对于前者,WCF通过HttpModule实现了服务的寄宿,而对于后者,WCF的服务寄宿通过一个HttpHandler实现。只有在ASP.NET兼容模式下,我们熟悉的一些ASP.NET机制才能被我们使用,比如通过HttpContext的请求下下文;基于文件或者Url的授权;HttpModule扩展;身份模拟(Impersonation)等。
  由于在ASP.NET兼容模式下,ASP.NET采用与.aspx Page完全一样的方式处理基于.svc的请求,换言之,我们就可以借助当前HttpContext的SessionState维护会话状态,进而创建一个支持会话的WCF Service。接下来,我们就通过一个简单的例子,一步步地创建这样的会话服务。本案例采用如图1所示的3层结构。 (Source Code从这里下载)
  
  
1.jpeg
   图1 ASP.NET兼容模式案例应用结构   
  步骤一、定义服务契约:ICalculator
  案例依然沿用计算服务的例子,不过通过原来直接与传入操作数并得到运算结果的方式不同,为了体现会话状态的存在,我们将本案例的WCF服务定义成“累积计算服务”:保留上一次运算的结果,并将其作为后续运算的操作数。为此,定义了如下一个接口作为服务契约:前面4个操作代表基本的加、减、乘、除运算,计算结果通过GetResult方法获得。
         
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>using System.ServiceModel;
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>namespace Artech.AspCompatibleServices.Contracts
复制代码
  
  1.    3: {
复制代码
  
  1.    4:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>[ServiceContract]
复制代码
  
  1.    5:     <services>public interface ICalculator
复制代码
  
  1.    6:     {
复制代码
  
  1.    7:         <endpoint binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator" />[OperationContract]
复制代码
  
  1.    8:       </service>  void Add(double x);
复制代码
  
  1.    7:                 name="CalculatorService"/>    </services>    [OperationContract]
复制代码
  
  1.   10:   </system.serviceModel>      void Subtract(double x);
复制代码
  
  1.   11: </configuration>        [OperationContract]
复制代码
  
  1.    8:         </client>void Multiply(double x);
复制代码
  
  1.    9:         <bindings>[OperationContract]
复制代码
  
  1.   10:         <wsHttpBinding>        void Divide(double x);
复制代码
  
  1.   11:             <binding name="CookieAllowableBinding" allowCookies="true"/>        [OperationContract]
复制代码
  
  1.   16:         double GetResult();
复制代码
  
  1.   17:     }
复制代码
  
  1.   18: }
复制代码
步骤二、实现服务:CalculatorService
服务的实现和.svc都定义在一个ASP.NET Web站点项目中。对于定义在 CalculatorService中的每次运算,先通过HttpContext从SessionState中取出上一次运算的结果,完成运算后再将新的运算结果保存到SessionState中。通过在CalculatorService上应用AspNetCompatibilityRequirementsAttribute实现对ASP.NET兼容模式的支持。
     
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>using System.ServiceModel.Activation;
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>using System.Web;
复制代码
  
  1.    3: using Artech.AspCompatibleServices.Contracts;
复制代码
  
  1.    4: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
复制代码
  
  1.    5: public class CalculatorService : ICalculator
复制代码
  
  1.    6: {
复制代码
  
  1.    7:     public void Add(double x)
复制代码
  
  1.    8:     {
复制代码
  
  1.    7:                 name="CalculatorService"/>    </services>    HttpContext.Current.Session["__Result"] = GetResult() + x;
复制代码
  
  1.   10:   </system.serviceModel>  }
复制代码
  
  1.   11: </configuration>    public void Subtract(double x)
复制代码
  
  1.   12:     {
复制代码
  
  1.    9:         <bindings>HttpContext.Current.Session["__Result"] = GetResult() - x;
复制代码
  
  1.   10:         <wsHttpBinding>    }
复制代码
  
  1.   11:             <binding name="CookieAllowableBinding" allowCookies="true"/>    public void Multiply(double x)
复制代码
  
  1.   16:     {
复制代码
  
  1.   17:         HttpContext.Current.Session["__Result"] = GetResult() * x;
复制代码
  
  1.   18:     }
复制代码
  
  1.   19:     public void Divide(double x)
复制代码
  
  1.   20:     {
复制代码
  
  1.   21:         HttpContext.Current.Session["__Result"] = GetResult() / x;
复制代码
  
  1.   22:     }
复制代码
  
  1.   23:     public double GetResult()
复制代码
  
  1.   24:     {
复制代码
  
  1.   25:         if (HttpContext.Current.Session["__Result"] == null)
复制代码
  
  1.   26:         {
复制代码
  
  1.   27:             HttpContext.Current.Session["__Result"] = 0.0;
复制代码
  
  1.   28:         }
复制代码
  
  1.   29:         return (double)HttpContext.Current.Session["__Result"];
复制代码
  
  1.   30:     }
复制代码
  
  1.   31: }
复制代码
下面是CalculatorService对应的.svc的定义和Web.config。为了简洁,在指令中,仅仅设置一个必需属性Service。对于ASP.NET兼容模式的支持,配置必不可少。
     
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>
复制代码
  
  1.    3:   <system.serviceModel>
复制代码
  
  1.    4:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
复制代码
  
  1.    5:     <services>
复制代码
  
  1.    6:       <service name="CalculatorService">
复制代码
  
  1.    7:         <endpoint binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator" />
复制代码
  
  1.    8:       </service>
复制代码
  
  1.    7:                 name="CalculatorService"/>    </services>
复制代码
  
  1.   10:   </system.serviceModel>
复制代码
  
  1.   11: </configuration>
复制代码
步骤三、创建客户端:Client
CalculatorService的客户端应用通过一个Console应用程序模拟,其服务调用方式并无特别之处,下面是相关的代码和配置。
     
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>using System;
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>using System.ServiceModel;
复制代码
  
  1.    3: using Artech.AspCompatibleServices.Contracts;
复制代码
  
  1.    4: namespace Artech.AspCompatibleServices.Clients
复制代码
  
  1.    5: {
复制代码
  
  1.    6:     class Program
复制代码
  
  1.    7:     {
复制代码
  
  1.    8:       </service>  static void Main(string[] args)
复制代码
  
  1.    7:                 name="CalculatorService"/>    </services>    {
复制代码
  
  1.   10:   </system.serviceModel>          using (ChannelFactory channelFactory = new ChannelFactory("CalculatorService"))
复制代码
  
  1.   11: </configuration>            {
复制代码
  
  1.    8:         </client>        ICalculator proxy = channelFactory.CreateChannel();
复制代码
  
  1.    9:         <bindings>        Console.WriteLine("初始值为: {0}", proxy.GetResult()); proxy.Add(1);
复制代码
  
  1.   10:         <wsHttpBinding>                Console.WriteLine("Add(3)", proxy.GetResult());
复制代码
  
  1.   11:             <binding name="CookieAllowableBinding" allowCookies="true"/>                Console.WriteLine("运算结果为: {0}", proxy.GetResult()); proxy.Multiply(10);
复制代码
  
  1.   16:                 Console.WriteLine("Multiply(10)", proxy.GetResult()); Console.WriteLine("运算结果为: {0}", proxy.GetResult()); proxy.Subtract(2);
复制代码
  
  1.   17:                 Console.WriteLine("Subtract(2)", proxy.GetResult()); Console.WriteLine("运算结果为: {0}", proxy.GetResult());
复制代码
  
  1.   18:             } Console.Read();
复制代码
  
  1.   19:         }
复制代码
  
  1.   20:     }
复制代码
  
  1.   21: }
复制代码
 
     
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>
复制代码
  
  1.    3:   <system.serviceModel>  
复制代码
  
  1.    4:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>   
复制代码
  
  1.    5:     <services>        
复制代码
  
  1.    8:       </service>              
复制代码
  
  1.    7:                 name="CalculatorService"/>
复制代码
但是,但我们运行客户端的程序,输出的结果并不像我们希望的那样。从下面的结果可以看出,每次通过GetResult()方法得到的结果都是0,也就是说,服务端并没有将运算结果保存下来。
     
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>初始值为:0
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>Add(3)运算结果为:0
复制代码
  
  1.    3: Multiply(10)运算结果为:0初始值为:0
复制代码
  
  1.    4: Subtract(2)运算结果为:0Add(3)运算结果为:0
复制代码
允许Cookie传递
要解释这个问题,得从Session的实现机制说起。众所周知,HTTP是无状态(Stateless)的传输协议,对服务端来说,它收到的每个HTTP请求都是全新的请求。ASP.NET会话(Session)的实现很简单,就是让每次HTTP请求携带Session的识别信息(Session ID),那么服务就可以根据此信息判断请求来自哪个客户端了。关于Session识别信息的保存,ASP.NET有两种方式:Cookie和URL,前者将其放到Cookie中,每次HTTP请求将会携带该Cookie的值,后者则将其作为请求URL的一部分。一般情况下采用基于Cookie的实现机制,如果Cookie禁用则采用后者。
那么对于ASP.NET兼容模式下的WCF也一样,要想让服务端能够识别会话,就需要让每个服务调用的HTTP请求携带Session的识别信息,我们也可以通过传递Cookie的方式来解决这个问题。对于WCF来说,Cookie传递能够通过Binding来控制,对于WsHttpBinding来说,默认情况下并不允许Cookie的传递。我们可以通过WsHttpBinding的AllowCookies来控制是否允许传递Cookie,该属性可以通过配置进行设置。为此,我们对客户端的配置进行了如下的修改。再次运行我们的案例程序,将会得到你期望的输出。
     
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>
复制代码
  
  1.    3:   <system.serviceModel>  
复制代码
  
  1.    4:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>   
复制代码
  
  1.    5:     <services>        
复制代码
  
  1.    8:       </service>  
复制代码
  
  1.    7:                 name="CalculatorService"/>    </services>   
复制代码
  
  1.   10:   </system.serviceModel>      
复制代码
  
  1.   11: </configuration>            
复制代码
  
  1.    8:         </client>
复制代码
  
  1.    9:         <bindings>
复制代码
  
  1.   10:         <wsHttpBinding>
复制代码
  
  1.   11:             <binding name="CookieAllowableBinding" allowCookies="true"/>
复制代码
客户端输出结果:
     
  1.    3: Multiply(10)运算结果为:0<?xml version="1.0" encoding="utf-8" ?><?xml version="1.0"?>初始值为:0
复制代码
  
  1.    4: Subtract(2)运算结果为:0<configuration><configuration>Add(3)运算结果为:3
复制代码
  
  1.   14: </system.serviceModel>
复制代码
  
  1.   15: </configuration>
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册