WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务
原文:WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务
在《基于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从这里下载)
步骤一、定义服务契约:ICalculator
案例依然沿用计算服务的例子,不过通过原来直接与传入操作数并得到运算结果的方式不同,为了体现会话状态的存在,我们将本案例的WCF服务定义成“累积计算服务”:保留上一次运算的结果,并将其作为后续运算的操作数。为此,定义了如下一个接口作为服务契约:前面4个操作代表基本的加、减、乘、除运算,计算结果通过GetResult方法获得。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: using System.ServiceModel;
2: namespace Artech.AspCompatibleServices.Contracts
3: {
4: [ServiceContract]
5: public interface ICalculator
6: {
7: [OperationContract]
8: void Add(double x);
9: [OperationContract]
10: void Subtract(double x);
11: [OperationContract]
12: void Multiply(double x);
13: [OperationContract]
14: void Divide(double x);
15: [OperationContract]
16: double GetResult();
17: }
18: }
步骤二、实现服务:CalculatorService
服务的实现和.svc都定义在一个ASP.NET Web站点项目中。对于定义在 CalculatorService中的每次运算,先通过HttpContext从SessionState中取出上一次运算的结果,完成运算后再将新的运算结果保存到SessionState中。通过在CalculatorService上应用AspNetCompatibilityRequirementsAttribute实现对ASP.NET兼容模式的支持。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: using System.ServiceModel.Activation;
2: using System.Web;
3: using Artech.AspCompatibleServices.Contracts;
4: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
5: public class CalculatorService : ICalculator
6: {
7: public void Add(double x)
8: {
9: HttpContext.Current.Session["__Result"] = GetResult() + x;
10: }
11: public void Subtract(double x)
12: {
13: HttpContext.Current.Session["__Result"] = GetResult() - x;
14: }
15: public void Multiply(double x)
16: {
17: HttpContext.Current.Session["__Result"] = GetResult() * x;
18: }
19: public void Divide(double x)
20: {
21: HttpContext.Current.Session["__Result"] = GetResult() / x;
22: }
23: public double GetResult()
24: {
25: if (HttpContext.Current.Session["__Result"] == null)
26: {
27: HttpContext.Current.Session["__Result"] = 0.0;
28: }
29: return (double)HttpContext.Current.Session["__Result"];
30: }
31: }
下面是CalculatorService对应的.svc的定义和Web.config。为了简洁,在<@ServiceHost%>指令中,仅仅设置一个必需属性Service。对于ASP.NET兼容模式的支持,配置<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>必不可少。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
5: <services>
6: <service name="CalculatorService">
7: <endpoint binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator" />
8: </service>
9: </services>
10: </system.serviceModel>
11: </configuration>
步骤三、创建客户端:Client
CalculatorService的客户端应用通过一个Console应用程序模拟,其服务调用方式并无特别之处,下面是相关的代码和配置。
1: using System;
2: using System.ServiceModel;
3: using Artech.AspCompatibleServices.Contracts;
4: namespace Artech.AspCompatibleServices.Clients
5: {
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("CalculatorService"))
11: {
12: ICalculator proxy = channelFactory.CreateChannel();
13: Console.WriteLine("初始值为: {0}", proxy.GetResult()); proxy.Add(1);
14: Console.WriteLine("Add(3)", proxy.GetResult());
15: Console.WriteLine("运算结果为: {0}", proxy.GetResult()); proxy.Multiply(10);
16: Console.WriteLine("Multiply(10)", proxy.GetResult()); Console.WriteLine("运算结果为: {0}", proxy.GetResult()); proxy.Subtract(2);
17: Console.WriteLine("Subtract(2)", proxy.GetResult()); Console.WriteLine("运算结果为: {0}", proxy.GetResult());
18: } Console.Read();
19: }
20: }
21: }
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <client>
5: <endpoint address="http://localhost/AspCompatibleServices/CalculatorService.svc"
6: binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator"
7: name="CalculatorService"/>
8: </client> </system.serviceModel>
9: </configuration>
但是,但我们运行客户端的程序,输出的结果并不像我们希望的那样。从下面的结果可以看出,每次通过GetResult()方法得到的结果都是0,也就是说,服务端并没有将运算结果保存下来。
1: 初始值为:0
2: Add(3)运算结果为:0
3: Multiply(10)运算结果为:0
4: Subtract(2)运算结果为: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,该属性可以通过配置进行设置。为此,我们对客户端的配置进行了如下的修改。再次运行我们的案例程序,将会得到你期望的输出。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <client>
5: <endpoint address="http://localhost/AspCompatibleServices/CalculatorService.svc"
6: binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator"
7: name="CalculatorService" bindingConfiguration="CookieAllowableBinding"/>
8: </client>
9: <bindings>
10: <wsHttpBinding>
11: <binding name="CookieAllowableBinding" allowCookies="true"/>
12: </wsHttpBinding>
13: </bindings>
14: </system.serviceModel>
15: </configuration>
客户端输出结果:
1: 初始值为:0
2: Add(3)运算结果为:3
3: Multiply(10)运算结果为:30
4: Subtract(2)运算结果为:28
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务的更多相关文章
- WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]
原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...
- WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...
- WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)
原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...
- WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...
- WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成
原文:WCF技术剖析之七:如何实现WCF与EnterLib PIAB.Unity之间的集成 在这之前,我写过深入介绍MS EnterLib PIAB的文章(参阅<MS Enterprise Li ...
- 《WCF技术剖析》博文系列汇总[持续更新中]
原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...
- WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构
原文:WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构 细算起来,已经有好几个月没有真正的写过文章了.近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析>的写作,一直 ...
- WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效
原文:WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效 本篇文章来源于几天前一个朋友向我咨询的问题.问题是这样的,他说他采用ASP.NET应用 ...
- WCF技术剖析之二十一:WCF基本异常处理模式[中篇]
原文:WCF技术剖析之二十一:WCF基本异常处理模式[中篇] 通过WCF基本的异常处理模式[上篇], 我们知道了:在默认的情况下,服务端在执行某个服务操作时抛出的异常(在这里指非FaultExcept ...
随机推荐
- 转: css实现垂直居中的方法
利用 CSS 来实现对象的垂直居中有许多不同的方法,比较难的是选择那个正确的方法.我下面说明一下我看到的好的方法和怎么来创建一个好的居中网站. 用 CSS 实现垂直居中并不容易.有些方法在一些浏览器中 ...
- mysqld守护进程
1.安装方式:安装文件:可执行的二进制文件: 源代码编译. 2.版本选择:常见版本区别:GA(一般应用,尽量使用最新版本)/RC(候选发布版本)/测试版本实版本选择主要是够用.适用.好用!不一定是最新 ...
- BZOJ 1631: [Usaco2007 Feb]Cow Party
题目 1631: [Usaco2007 Feb]Cow Party Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 491 Solved: 362[Sub ...
- SQL查询各阶段的统计信息
我们经常会遇到各种分类统计问题,须要将这些结果一次显示出来.这次老师提出的要求是我想看60分下面多少人.60~70多少人,70~80多少人.80~90多少人,90~100多少人.他们曾 ...
- c++, 虚基派生 : 共同基类产生的二义性的解决办法
虚基派生 //虚继承 #include <iostream> using namespace std; #include <string> //---------------- ...
- Android 中 GridView 常用属性合集
GridView的一些常用属性: android:numColumns=”auto_fit” //GridView的列数设置为自动 android:columnWidth=”90dp " / ...
- XCode 快速注释插件
下载VVDocumenter-Xcode这个插件 打开编译后,重启Xcode 输入///,如效果图,Window->VVDocumenter自己定制风格
- c# 数据库编程(利用DataSet 和 DataAdaper对象操作数据库--单表操作)
一.概述 前面2篇文章,介绍了使用SqlCommand对象利用sql命令来操作数据库. 这篇文章我们来介绍使用c#的DataSet 和 DataAdaper对象操作操作数据库. 先来介绍下这两个对象是 ...
- Bugzilla使用手册及解决方案
Bugzilla使用手册 Bugzilla 是一个开源的缺陷跟踪系统(Bug-Tracking System),它可以管理软件开发中缺陷的提交(new),修复(resolve),关闭(close)等整 ...
- pojg2744找一个最长的字符串x,使得对于已经给出的字符串中的任意一个y,x或者是y的子串,或者x中的字符反序之后得到的新字符串是y的子串。
http://poj.grids.cn/practice/2744 描述现在有一些由英文字符组成的大小写敏感的字符串,你的任务是找到一个最长的字符串x,使得对于已经给出的字符串中的任意一个y,x或者是 ...