wcf 透明代理代理 是怎么工作的

WCF 代理 是怎么工作的?用代码说话 - Chester.Y.Zhang - 推酷
WCF 代理 是怎么工作的?用代码说话 - Chester.Y.Zhang
1.WCF生成代理的方式
2.WCF代理原理
第一个问题引用 一篇Robin's博文[
] 讲述了创建客户端服务对象的方法
1.代理构造法
a.开启服务后,添加服务引用
b.知道元数据地址,通过svcutli.exe生成代理类和配置文件
c.从服务契约DLL中导出元数据,然后更具本地的元数据文件生成代理类和配置文件
d.知道元数据地址,自己编写代码生成(使用ServiceContractGenerator类等),生成代理类和配置文件
2.通道工厂(ChannelFactory&T&)
a.知道终结点地址,绑定协议(ABC中的A和B)
b.只知道元数据终结点地址(代码中使用MetadataResover类获取服务信息)
文章最后附有代码:代码,可以下下来运行测试等。
下边来看看生成的代理是什么样的。
1.添加服务引用 生成了一个 继承自 System.ServiceModel.ClientBase&Wcf.Client.ServiceReference1.IService& 的 ServiceClient
public interface IService {
[System.ServiceModel.OperationContractAttribute(Action=&http://tempuri.org/IService/DoWork&, ReplyAction=&http://tempuri.org/IService/DoWorkResponse&)]
void DoWork();
[System.ServiceModel.OperationContractAttribute(Action=&http://tempuri.org/IService/GetData&, ReplyAction=&http://tempuri.org/IService/GetDataResponse&)]
Wcf.Client.ServiceReference1.MyData GetData(int field);
[System.Diagnostics.DebuggerStepThroughAttribute()]
[piler.GeneratedCodeAttribute(&System.ServiceModel&, &4.0.0.0&)]
public partial class ServiceClient : System.ServiceModel.ClientBase&Wcf.Client.ServiceReference1.IService&, Wcf.Client.ServiceReference1.IService {
public ServiceClient() {
public ServiceClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
public ServiceClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
public ServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
public ServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
public void DoWork() {
base.Channel.DoWork();
public Wcf.Client.ServiceReference1.MyData GetData(int field) {
return base.Channel.GetData(field);
生成的代码中有接口,服务客户端(ServiceClient)
调用DoWork()和GetData(int field) 方法,是调用的 ClientBase&T&中Channel对象的DoWork()和GetData(field)方法
ClientBase&T&
public abstract class ClientBase&TChannel& : ICommunicationObject, IDisposable where TChannel : class
……//其他内容
protected TChannel Channel { get; }
public ChannelFactory&TChannel& ChannelFactory { get; }
Channel 的类型是TChannel ,同时可以发现内部有个ChannelFactory。
2.使用ChannelFactory&T&
ChannelFactory&Proxys.IService& channelFactory = new ChannelFactory&Proxys.IService&(bind);
var channel = channelFactory.CreateChannel(address);
using (channel as IDisposable)
channel.DoWork();
Wcf.Proxys.MyData myData = channel.GetData(10);
每次我们看代码都只能看到这里,却不能知道ClientBase&T&,ChannelFatctory&T&是怎么起到代理作用的,怎么把方法的调用转换成底层的交互的。
我们来找找源头:(本来是想反编译ServiceModel.dll,翻遍以后有不能看到代码内容,好在Mono下有相应的模块内容,可以在“开源中国社区”看到一些代码)
ClientBase&T& Mono开源代码
protected TChannel Channel {
get { return (TChannel) (object) InnerC }
看到212行代码,channel返回的属性InnerChannel
public IClientChannel InnerChannel {
205  get {
if (inner_channel == null)
inner_channel = (IClientChannel) (object) CreateChannel ();
return inner_
通过CreateChannel ()创建的对象
protected virtual TChannel CreateChannel ()
return ChannelFactory.CreateChannel ();
CreateChannel ()方法是用ChannelFactory.CreateChannel (); 创建对象
public ChannelFactory&TChannel& ChannelFactory {
get { return }
internal set {
factory.OwnerClientBase = this;
跳转到 ChannelFactory&T&类
public TChannel CreateChannel ()
EnsureOpened ();
return CreateChannel (Endpoint.Address);
CreateChannel方法的一个重载方法
public virtual TChannel CreateChannel (EndpointAddress address, Uri via)
   #if MONOTOUCH
throw new InvalidOperationException (&MonoTouch does not support dynamic proxy code generation. Override this method or its caller to return specific client proxy instance&);
var existing = Endpoint.A
Endpoint.Address =
EnsureOpened ();
Endpoint.Validate ();
Type type = ClientProxyGenerator.CreateProxyType (typeof (TChannel), Endpoint.Contract, false);
// in .NET and SL2, it seems that the proxy is RealProxy.
// But since there is no remoting in SL2 (and we have
// no special magic), we have to use different approach
// that should work either.
object proxy = Activator.CreateInstance (type, new object [] {Endpoint, this, address ?? Endpoint.Address, via});
return (TChannel)
} catch (TargetInvocationException ex) {
if (ex.InnerException != null)
throw ex.InnerE
} finally {
Endpoint.Address =
  #endif
翻一下一下注释:在.NET和SL2.0中,看来好像这个代理是真实代理,因为在SL2.0中没有是用Remoting(并且也没有独特魔力),我们必须是用不同的方式使它依然能够工作运行起来。
这里注意两点:
1.Type type = ClientProxyGenerator.CreateProxyType (typeof (TChannel), Endpoint.Contract, false);
2.object proxy = Activator.CreateInstance (type, new object [] {Endpoint, this, address ?? Endpoint.Address, via});
先来看看简单一点的第二个方法的解释:
对该Activator.CreateInstance(type,object[]) 的解释是”使用与指定参数匹配程度最高的构造函数创建指定类型的实例”&&
两个参数分别是:需要生成的对象的类;构造函数传入的参数,系统会更具参数的数量、顺序和类型的匹配程度调用相应的构造函数。
看看第一个方法原型是什么样子:
public static Type CreateProxyType (Type requestedType, ContractDescription cd, bool duplex)
ClientProxyKey key = new ClientProxyKey (requestedType, cd, duplex);
lock (proxy_cache) {
if (proxy_cache.TryGetValue (key, out res))
string modname = &dummy&;
Type crtype =
#if !NET_2_1
duplex ? typeof (DuplexClientRuntimeChannel) :
typeof (ClientRuntimeChannel);
// public class __clientproxy_MyContract : (Duplex)ClientRuntimeChannel, [ContractType]
var types = new List&Type& ();
types.Add (requestedType);
if (!cd.ContractType.IsAssignableFrom (requestedType))
types.Add (cd.ContractType);
if (cd.CallbackContractType != null && !cd.CallbackContractType.IsAssignableFrom (requestedType))
types.Add (cd.CallbackContractType);
CodeClass c = new CodeModule (modname).CreateClass (&__clientproxy_& + cd.Name, crtype, types.ToArray ());
// public __clientproxy_MyContract (
ServiceEndpoint arg1, ChannelFactory arg2, EndpointAddress arg3, Uri arg4)
: base (arg1, arg2, arg3, arg4)
Type [] ctorargs = new Type [] {typeof (ServiceEndpoint), typeof (ChannelFactory), typeof (EndpointAddress), typeof (Uri)};
CodeMethod ctor = c.CreateConstructor (
MethodAttributes.Public, ctorargs);
CodeBuilder b = ctor.CodeB
MethodBase baseCtor = crtype.GetConstructors (
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) [0];
if (baseCtor == null) throw new Exception (&INTERNAL ERROR: ClientRuntimeChannel.ctor() was not found.&);
ctor.GetThis (),
new CodeArgumentReference (typeof (ServiceEndpoint), 1, &arg0&),
new CodeArgumentReference (typeof (ChannelFactory), 2, &arg1&),
new CodeArgumentReference (typeof (EndpointAddress), 3, &arg2&),
new CodeArgumentReference (typeof (Uri), 4, &arg3&));
res = CreateProxyTypeOperations (crtype, c, cd);
lock (proxy_cache) {
proxy_cache [key] =
注意内容:
1.内部使用了缓存。
2.使用了动态编译.
3.根据具不同的duplex [in]生成不同的类(代理)类型:
4.看看res = CreateProxyTypeOperations (crtype, c, cd); 这句话发生了什么
protected static Type CreateProxyTypeOperations (Type crtype, CodeClass c, ContractDescription cd)
// member implementation
BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.I
foreach (OperationDescription od in cd.Operations) {
// FIXME: handle properties and events.
#if !NET_2_1
if (od.SyncMethod != null)
GenerateMethodImpl (c, crtype.GetMethod (&Process&, bf), od.Name, od.SyncMethod);
if (od.BeginMethod != null)
GenerateBeginMethodImpl (c, crtype.GetMethod (&BeginProcess&, bf), od.Name, od.BeginMethod);
if (od.EndMethod != null)
GenerateEndMethodImpl (c, crtype.GetMethod (&EndProcess&, bf), od.Name, od.EndMethod);
Type ret = c.CreateType ();
static void GenerateEndMethodImpl (CodeClass c, MethodInfo endProcessMethod, string name, MethodInfo mi)
CodeMethod m = c.ImplementMethod (mi);
CodeBuilder b = m.CodeB
ParameterInfo [] pinfos = mi.GetParameters ();
ParameterInfo p = pinfos [0];
CodeArgumentReference asyncResultRef = m.GetArg (0);
CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), &parameters&);
b.CurrentBlock.Add (paramsDecl);
CodeVariableReference paramsRef = paramsDecl.V
b.Assign (paramsRef,
new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length - 1)));
for (int i = 0; i & pinfos.Length - 2; i++) {
ParameterInfo par = pinfos [i];
if (!par.IsOut)
b.Assign (
new CodeArrayItem (paramsRef, new CodeLiteral (i)),
new CodeCast (typeof (object),
new CodeArgumentReference (par.ParameterType, par.Position + 1, &arg& + i)));
#if USE_OD_REFERENCE_IN_PROXY
CodePropertyReference argMethodInfo = GetOperationMethod (m, b, name, &EndMethod&);
CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), &GetCurrentMethod&);
CodeLiteral argOperName = new CodeLiteral (name);
CodeVariableReference retValue = null;
if (mi.ReturnType == typeof (void))
b.Call (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef);
CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, &retValue&);
b.CurrentBlock.Add (retValueDecl);
retValue = retValueDecl.V
b.Assign (retValue,
new CodeCast (mi.ReturnType,
b.CallFunc (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef)));
// FIXME: fill out parameters
if (retValue != null)
b.Return (retValue);
CreateProxyTypeOperations& 看方法名就大概清楚:创建代理的操作(方法)。
GenerateEndMethodImpl& 方法的具体实现。(最后这个方法很重要,不过部分代码我没有看明白,还请看明白的人还望不吝赐教)
ClientBase&T& 内部使用了 ChannelFactory&T&
这里使用的Mono代码说明、如果与.Net有区别也是有可能的。
至少基本说明了一个问题,代理是更具描述(元数据、接口等来源)动态生成接口的对象,
该对象的方法体(方法名、参数、返回值)都与接口一致,方法体对方法传入的值进行了处理。
只要获取到了传递过来的方法、参数、返回值信息等,就可以想怎么样就怎么样、为所欲为了。
C#客户端在调用WCF服务的时候,很多情况使用Remoting下RealProxy,RealProxy具有一个抽象的Invoke方法:public abstract IMessage Invoke(IMessage msg);
MSDN对RealProxy.Invoke(IMessage msg)方法的解释是
当调用受 RealProxy 支持的透明代理时,它将调用委托给 Invoke 方法。 Invoke 方法将 msg 参数中的消息转换为 IMethodCallMessage,并将其发送至 RealProxy 的当前实例所表示的远程对象。
public class CalculatorServiceRealProxy : RealProxy
public CalculatorServiceRealProxy():base(typeof(ICalculatorService)){}
public override IMessage Invoke(IMessage msg)
IMethodReturnMessage methodReturn = null;
IMethodCallMessage methodCall = (IMethodCallMessage)
var client = new ChannelFactory&ICalculatorService&(&CalculatorService&);
var channel = client.CreateChannel();
object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
methodCall.Args.CopyTo(copiedArgs, 0);
object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);
methodReturn = new ReturnMessage(returnValue,
copiedArgs,
copiedArgs.Length,
methodCall.LogicalCallContext,
methodCall);
//TODO:Write log
catch (Exception ex)
var exception =
if (ex.InnerException != null)
exception = ex.InnerE
methodReturn = new ReturnMessage(exception, methodCall);
var commObj = channel as ICommunicationO
if (commObj != null)
commObj.Close();
catch (CommunicationException)
commObj.Abort();
catch (TimeoutException)
commObj.Abort();
catch (Exception)
commObj.Abort();
//TODO:Logging exception
return methodR
static void Main(string[] args)
ICalculatorService proxy = (ICalculatorService)new CalculatorServiceRealProxy().GetTransparentProxy();
Console.WriteLine(&x + y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Add(1, 2));
Console.WriteLine(&x - y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Subtract(1, 2));
Console.WriteLine(&x * y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Multiply(1, 2));
Console.WriteLine(&x / y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Divide(1, 2));
Console.ReadKey();
ICalculatorService proxy = (ICalculatorService)new CalculatorServiceRealProxy().GetTransparentProxy();& 获取到动态“实现”ICalculatorService& 的 实体对象,
每次调用的时候都会调用实现的RealProxy的Invoke方法
IMethodCallMessage methodCall = (IMethodCallMessage)& 把msg转换成 IMethodCallMessage ;
var channel = client.CreateChannel();这句话创建了代理对象;
object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs); 执行这个代理对象;
methodReturn = new ReturnMessage(returnValue,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& copiedArgs,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& copiedArgs.Length,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& methodCall.LogicalCallContext,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& methodCall); 返回值处理
这样的情况下 使用了两次代理。
这样做就具有了AOP的特征,好处也很多
1.做日志记录;
2.异常处理;
3.这个地方做了所有的远程连接操作,对调用这来讲,与通常的非服务调用没有区别,比如这里的“关闭连接”;
上边的代码值得优化一下,比如:
1.CalculatorServiceRealProxy 应该改成CalculatorServiceRealProxy &T&,ICalculatorService 通过 T传入,这样灵活性增加不少。
2.endpointConfigurationName 应该也放到CalculatorServiceRealProxy 的构造函数上;或者系统做默认规则处理,比如:配置节点名称就是去掉相应接口名称前的“I”,等等。
希望与大家多多交流& QQ:;群Q:
如果有用请大伙儿帮忙推荐一下,谢谢!
03:00:43(待续)
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致在.NET 3.0 SP1中,WCF客户端创建有一个重要的性能改进。对BasicHttpBinding 来说,性能已经接近于创建ASMX代理。ASMX 代理 vs WCF 代理:ASMX代理比WCF代理更简单。前者是类型System.Web.Services.Protocols.SoapHttpClientProtocol的一个包装。在ASMX世界中,编程模型是两条平行线。
Json 凭借其自身的优势,在Web数据处理方面已经占据一定的位置,这段时间涉及到用Json做为数据传输格式的项目有3个,其中有部分页面就采用了Json 数据传输格式, 这里我总结下这段时间采用这种方式的一些问题总结。向客户端提供JSON数据的方式有:用WCF提供Json数据:用WCF向客户端提供Json数据需要注意
最近做了一个GIS方面的网站,用到了SilverLight、WCF、WF方面的功能,当把想法发给其他人时,在项目的发布上出现了很多问题,这里简述一下解决方法。SliverLight版本问题:重新配置环境后,会经常发现Visual Studio 提示类似“无法启动调试。未安装Silverlight Developer运行时。
在《通过一个模拟程序让你明白ASP.NET MVC是如何运行的》一文中我通过一个普通的ASP.NET Web程序模拟了ASP.NET MVC的执行流程,现在我们通过类似的原理创建一个用于模拟WCF服务端和客户端工作原理的模拟程序。基本的组件和执行流程:我们只模拟WCF完成一个简单的服务调用所必需的组件和流程。
REST定义了应该如何正确地使用(这和大多数人的实际使用方式有很大不同)Web标准,例如HTTP和URI。如果你在设计应用程序时能坚持REST原则,那就预示着你将会得到一个使用了优质Web架构(这将让你受益)的系统在这里因为要配置Rest服务,所以我们添加webHttp Behavior,并且启用helpEnabled.
本文列举的内容是我开始用Silverlight做开发以来收集起来的,如果你打算做一名全职的Silverlight开发人员,那更应该将这些内容熟记于心,放心
虽然客户/服务间的通信已经能够正常工作,而且服务返回的结果也和我们想象的一样,但服务是不安全的。在布署服务时,安全性是一个主要的考虑因素,而WCF使得对服务进行保护变得非常容易。
在这个练习中,我们要把第一个练习中定义和实现的服务运行起来。更具体地说,我们会以一个.NET控制台程序为宿主程序来运行服务。
时间荏苒,伴随着新年的瑞雪我们迈入了2010年。回想笔者曾在08年底对09年的各种新技术和新趋势做过大胆预测,但09年发生了很多大事如Oralce收购Sun,微软推出Windows 7操作系统等等,使预测也出现了微妙的变化,下面我们来一一揭晓。
我们会在一个单独的.NET类库中定义和实现这个服务。虽然这使我们的实验变得简单,但一般来说,更好的方法是在一个程序集中定义服务契约并在另一个程序集中用一个实体类来实现该契约。这是契约优先开发的核心理念。
当内置wcf编码器无法满足我们的要求的时候,可以根据本文介绍的方法创建一个定制的编码器,然后将其作为插件使用即可。
WCF是Microsoft为构建面向服务的应用提供的分布式通信编程框架,是.NET Framework 3.5的重要组成部分。使用该框架,开发人员可以构建跨平台、安全、可靠和支持事务处理的企业级互联应用解决方案。
Windows Communication Foundation (WCF)是Microsoft为构建面向服务的应用提供的分布式通信编程框架,是.NET Framework 3.5的重要组成部分。使用该框架,开发人员可以构建跨平台、安全、可靠和支持事务处理的企业级互联应用解决方案。
当为ASP.NET AJAX程序创建一个新的WCF服务时,默认情况下该服务类前面使用AspNetCompatibilityRequirements属性进行修饰。现在,我们来作一下简要分析。
在.NET中,上下文(Context)的概念贯穿着.NET的很多核心内容。提供类似运行环境的服务和内容。在.NET中,基于上下文的拦截的技术却提供了很好很强大的功能。使我们能方便是实现类似AOP的编程模式
在文件系统存储中,我们讲述了LocalStorage的不可共享性,当时又为了学习体验Worker Role的用法,就产生了这样的一个想法
微软发布了托管服务引擎(Managed Services Engine, MSE)在2009年5月的CTP版,该版本的源代码可以在Codeplex中获取。该版本对2月的Beta版作了少量的更新。网站中对产品的描述为:
本文通过漫画的方式讲述了Visual Studio 2008的新特性。包括语言级集成查询LINQ,WCF,C#效率优化和更加强健的框架等。
现在我们已经定义了所有运行我们的TODO应用程序所需要的资料,是该以启用ASP.NET AJAX的WCF服务方式暴露服务给客户端的时候了。
这里,我们将看到一种简单明了的方式在JavaScript中调用一个WCF服务。右键点击Web应用程序工程并选择添加新项。选择启用AJAX的WCF服务项模板,并命名为“HelloWorldService.svc” 最后点击确定。通过一个模拟程序让你明白WCF大致的执行流程
在《》一文中我通过一个普通的ASP.NET Web程序模拟了ASP.NET MVC的执行流程,现在我们通过类似的原理创建一个用于模拟WCF服务端和客户端工作原理的模拟程序。[源代码从下载]
目录 一、基本的组件和执行流程 二、创建自定义HttpHandler实现对服务调用请求的处理 三、定义创建WCF组件的工厂 四、定义HttpModule映射WcfHandler 五、创建自定义的真实代理实现服务的调用 六、定义服务代理工厂 七、服务“寄宿”和调用
一、基本的组件和执行流程
我们只模拟WCF完成一个简单的服务调用所必需的组件和流程,右图反映了进行服务调用的必要步骤和使用的相关WCF组件。下面列出了服务端涉及的组件和流程:
请求消息的接收和回复消息的发送:服务端在传输层监听与接收来自客户的请求,并将经过编码后的回复消息通过传输层发送到客户端;
请求消息的解码和回复消息的编码:将接收到的字节数组通过解码生成请求消息对象,并将回复消息通过编码转化成字节数组。消息的编码和解码通过消息编码器(MessageEncoder)完成,而消息编码器工厂(MessageEncoderFactory)负责创建该对象;
请求消息的反序列化和回复消息的序列化:对请求消息进行反序列化,为服务操作的执行生成相应的输入参数,以及将服务操作执行的结果(返回值或输出/引用参数)序列化,并生成回复消息。序列化和反序列化通过分发消息格式化器(DispatchMessageFormatter)完成;
服务对象的创建:创建或激活服务对象实例,实例提供者(InstanceProvider)用于服务对象的创建或获取,本例直接通过反射创建服务实例;
服务操作的执行:调用创建的服务对象的操作方法,并传入经过反序列化生成的输入参数。操作调用器(OperationInvoker)完成对服务操作的最终执行。
相较于服务端的请求监听、消息接收、服务实例激活和操作调用流程,客户端的处理流程显得相对简单,仅仅包含以下3个必需的步骤:
请求消息的序列化和回复消息的反序列化:生成请求消息并将输入参数序列化到请求消息中,以及对回复消息进行反序列化,转化成方法调用的返回值或输出/引用参数。序列化和反序列化通过ClientMessageFormatter完成;
请求消息的编码和回复消息的解码:对请求消息进行编码生成字节数组供传输层发送,以及将传输层接收到的字节数组解码生成回复消息。消息的编码和解码通过消息编码器完成,而消息编码器工厂负责创建该对象;
请求消息的发送和回复消息的接收:在传输层将经过编码的请求消息发送到服务端,以及接收来自服务端的回复消息。
本实例的解决方法依然采用包含Service.Interface、Service和Client三个项目的结构,不过Service项目现在是一个Web应用。也就是说我们通过一个Web应用的方式实现WCF端对服务调用请求的整个处理流程。
二、创建自定义HttpHandler实现对服务调用请求的处理
对于一个ASP.NET Web应用来说,对请求的处理最终都落实到一个具体的HttpHandler对象上,所以我们通过实现接口System.Web.IHttpHandler自定义了如下一个WcfHandler用于处理针对WCF服务请求的处理。
1: public class WcfHandler: IHttpHandler
//其他成员
public Type ServiceType { private }
public MessageEncoderFactory MessageEncoderFactory { private }
public IDictionary&string, MethodInfo& Methods { private }
public IDictionary&string, IDispatchMessageFormatter& MessageFormatters { private }
public IDictionary&string, IOperationInvoker& OperationInvokers { private }
public bool IsReusable
get { return false; }
public WcfHandler(Type serviceType, MessageEncoderFactory messageEncoderFactory)
this.ServiceType = serviceT
this.MessageEncoderFactory = messageEncoderF
this.Methods = new Dictionary&string, MethodInfo&();
this.MessageFormatters = new Dictionary&string, IDispatchMessageFormatter&();
this.OperationInvokers = new Dictionary&string, IOperationInvoker&();
如上面代码所示,上述的关于WCF服务端框架所需的组件以只读属性的方式体现在WcfHandler上。ServiceType属性表示服务的类型,基于这个类型通过反射创建服务实例。消息编码器工厂通过MessageEncoderFactory属性表示,两个字典类型的属性MessageFormatters和OperationInvokers代表基于操作的分发消息格式化器和操作调用器列表,字典的Key为操作请求消息的&Action&报头的值。而Methods表示契约接口所有操作方法的MethodInfo集合。
针对WCF服务的请求处理实现在如下的ProcessRequest方法中,执行的逻辑也不算复杂。我们直接通过消息编码器工厂创建的消息编码从当前HTTP请求的输入流中读取出消息。然后根据当前消息的&Action&报头的值从MessageFormatters属性中找到与当前请求操作相匹配的分发消息格式化器对消息进行反序列化。
接着直接通过反射的方式根据服务类型创建服务实例对象。同样根据当前消息的&Action&报头从OperationInvokers属性获取出基于当前请求操作的操作调用器,并将创建的服务实例和反序列化后生成的参数作为输入执行操作方法。
操作的执行结果通过分发消息格式化器进行序列化生成的消息最终通过消息编码器写入当前HTTP回复的输出流中返回给客户端。
1: public class WcfHandler: IHttpHandler
//其他成员
public void ProcessRequest(HttpContext context)
//对HttpPRequest进行解码生成请求消息对象
Message request = this.MessageEncoderFactory.Encoder.ReadMessage(context.Request.InputStream, int.MaxValue, &application/soap+ charset=utf-8&);
//通过请求消息得到代表服务操作的Action
string action = request.Headers.A
//通过Action从MethodInfo字典中获取服务操作对应的MethodInfo对象
MethodInfo method = this.Methods[action];
//得到输出参数的数量
int outArgsCount = 0;
foreach (var parameter in method.GetParameters())
if (parameter.IsOut)
outArgsCount++;
//创建数组容器,用于保存请求消息反序列后生成的输入参数对象
int inputArgsCount = method.GetParameters().Length - outArgsC
object[] parameters = new object[inputArgsCount];
this.MessageFormatters[action].DeserializeRequest(request, parameters);
List&object& inputArgs = new List&object&();
object[] outArgs = new object[outArgsCount];
//创建服务对象,在WCF中服务对象通过InstanceProvider创建
object serviceInstance = Activator.CreateInstance(this.ServiceType);
//执行服务操作
object result = this.OperationInvokers[action].Invoke(serviceInstance,parameters, out outArgs);
//将操作执行的结果(返回值或者输出参数)序列化生成回复消息
Message reply = this.MessageFormatters[action].SerializeReply(request.Version, outArgs, result);
context.Response.ClearContent();
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.ContentType = &application/soap+ charset=utf-8&;
//对回复消息进行编码,并将编码后的消息通过HttpResponse返回
this.MessageEncoderFactory.Encoder.WriteMessage(reply, context.Response.OutputStream);
context.Response.Flush();
三、定义创建WCF组件的工厂
对于本例来说,客户端和服务端需要的组件主要有四类,即消息编码器工厂、分发消息格式化器、客户端消息格式化器和操作调用器。我们通过具有如下定义的静态的工厂类ComponentBuilder来创建它们。我们调用操作行为的GetFormatter方法来创建基于指定操作的消息格式化器。不过该方法是一个内部方法,所以我们是通过反射的方式来调用的。isProxy参数表示创建的是客户端消息格式化器(True)还是分发消息格式化器(False)。
消息编码器工厂通过基于文本编码方式绑定元素的CreateMessageEncoderFactory创建,传入的参数分别表示消息的版本和文本编码类型。我们采用SyncMethodInvoker以同步的方式进行操作的执行。由于SyncMethodInvoker是一个内部类型,所以我们不得不采用反射的方式来创建它。
1: public static class ComponentBuilder
public static object GetFormatter(OperationDescription operation, bool isProxy)
bool formatRequest = false;
bool formatReply = false;
DataContractSerializerOperationBehavior behavior = new DataContractSerializerOperationBehavior(operation);
MethodInfo method = typeof(DataContractSerializerOperationBehavior).GetMethod(&GetFormatter&, BindingFlags.Instance | BindingFlags.NonPublic);
return method.Invoke(behavior, new object[] { operation, formatRequest, formatReply, isProxy });
public static MessageEncoderFactory GetMessageEncoderFactory(MessageVersion messageVersion, Encoding writeEncoding)
TextMessageEncodingBindingElement bindingElement = new TextMessageEncodingBindingElement(messageVersion, writeEncoding);
return bindingElement.CreateMessageEncoderFactory();
public static IOperationInvoker GetOperationInvoker(MethodInfo method)
string syncMethodInvokerType = &System.ServiceModel.Dispatcher.SyncMethodInvoker, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c&;
Type type = Type.GetType(syncMethodInvokerType);
return (IOperationInvoker)Activator.CreateInstance(type, new object[]{method});
四、定义HttpModule映射WcfHandler
我们通过HttpModule的方式将用于处理WCF服务请求的映射到相应的WCF服务调用请求,为此我们定义了如下一个实现了System.Web.IHttpModule接口的WcfHttpModule类型。WcfHttpModule通过注册HttpApplication的BeginRequest事件的方式将创建的WcfHandler映射为处理当前HTTP请求的HttpHandler。
1: public class WcfHttpModule: IHttpModule
public void Dispose() {}
public void Init(HttpApplication context)
context.BeginRequest += (sender, args) =&
string relativeAddress = HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.Remove(0,2);
Type serviceType = RouteTable.Routes.Find(relativeAddress);
if (null == serviceType)
IHttpHandler handler = this.CreateHttpHandler(serviceType);
context.Context.RemapHandler(handler);
protected IHttpHandler CreateHttpHandler(Type serviceType)
MessageEncoderFactory encoderFactory = ComponentBuilder.GetMessageEncoderFactory(MessageVersion.Default, Encoding.UTF8);
WcfHandler handler = new WcfHandler(serviceType, encoderFactory);
Type interfaceType = serviceType.GetInterfaces()[0];
ContractDescription contract = ContractDescription.GetContract(interfaceType);
foreach (OperationDescription operation in contract.Operations)
IDispatchMessageFormatter messageFormatter = (IDispatchMessageFormatter)ComponentBuilder.GetFormatter(operation, false);
handler.MessageFormatters.Add(operation.Messages[0].Action, messageFormatter);
IOperationInvoker operationInvoker = ComponentBuilder.GetOperationInvoker(operation.SyncMethod);
handler.OperationInvokers.Add(operation.Messages[0].Action, operationInvoker);
handler.Methods.Add(operation.Messages[0].Action, operation.SyncMethod);
至于WcfHandler的创建,需要确定服务的类型。而服务的类型只能根据请求的地址来确定,这个IIS寄宿根据.svc文件来创建ServiceHost的原理是一样的。对于本例来说,我们需要对请求的地址和服务类型作一个映射,为此我们定义了如下一个RouteMapping的类型表示这个映射。
1: public class RouteMapping
public string Address { private }
public Type ServiceType { private }
public RouteMapping(string address, Type serviceType)
this.Address =
this.ServiceType = serviceT
而映射表则通过如下一个继承自Collection&RouteMapping&的RouteTable来定义。泛型的Register&T&方法用于注册地址与服务类型的映射关系,而Find方法则根据地址获取相应的服务类型。静态属性Routes表示当前被使用的映射表,而在WcfHttpModule中正是通过这个静态属性根据解析出来的地址得到用于创建WcfHandler的服务类型的。
1: public class RouteTable: Collection&RouteMapping&
public static RouteTable Routes{ private}
static RouteTable()
Routes = new RouteTable();
public Type Find(string address)
RouteMapping routeMapping =
(from route in this
where string.Compare(route.Address, address,true) == 0
select route).FirstOrDefault();
return null == routeMapping? null: routeMapping.ServiceT
public void Register&T&(string address)
this.Add(new RouteMapping(address, typeof(T)));
五、创建自定义的真实代理实现服务的调用
ChannelFactory&TChannel&创建的服务代理仅仅是一个透明代理,而真实实现服务调用的是它的真实代理。为此我们创建了如下一个继承自RealProxy的泛型的ServiceChannelProxy&TChannel&,其中泛型参数为契约接口类型。
1: public class ServiceChannelProxy&TChannel&: RealProxy
//其他成员
public Uri Address { private }
public MessageVersion MessageVersion { private }
public IDictionary&string, IClientMessageFormatter& MessageFormatters { private }
public MessageEncoderFactory MessageEncoderFactory { private }
public ServiceChannelProxy(Uri address, MessageVersion messageVersion, MessageEncoderFactory encoderFactory): base(typeof(TChannel))
this.Address =
this.MessageVersion = messageV
this.MessageEncoderFactory = encoderF
this.MessageFormatters = new Dictionary&string, IClientMessageFormatter&();
和WcfHttpHandler类似,进行服务调用所需的组件通过相应的只读属性表示。属性MessageEncoderFactory表示消息编码器工厂,而字典类型的MessageFormatters表示基于每个操作的客户端消息格式化器列表,其中的Key为操作的名称。属性Address表示被调用服务的地址。
针对透明代理的方法调用最终都会转移到针对真实真实代理的Invoke方法,所以我们将所有的服务调用操作实现在如下的Invoke方法中。我们首先获取代表当前调用方法的MethodBase上应用的OperationContractAttribute特性,并借此获得操作名称。
根据获取的操作名称从属性MessageFormatters属性中获得基于当前操作的客户端消息格式化器,并将方法调用转化消息。接着根据Address属性表示的服务调用地址创建EndpointAddress对象并将其附加到请求消息中。除此之外,还需要为请求消息添加一些必要的报头(比如&MessageId&和&ReplyTo&)。
接下来通过消息编码器工厂创建的消息编码器对消息进行编码,并将得到的字节数据通过创建的HttpWebRequest对象发送出去。对于得到的HttpWebResponse,则通过消息编码器进行解码以生成回复消息。回复消息最终通过客户端消息格式化器进行反序列化,得到的对象映射为方法返回值和输出/引用参数返回。
1: public class ServiceChannelProxy&TChannel&: RealProxy
//其他成员
public override IMessage Invoke(IMessage msg)
IMethodCallMessage methodCall = (IMethodCallMessage)
//得到操作名称
object[] attributes = methodCall.MethodBase.GetCustomAttributes(typeof(OperationContractAttribute), true);
OperationContractAttribute attribute = (OperationContractAttribute)attributes[0];
string operationName = string.IsNullOrEmpty(attribute.Name) ? methodCall.MethodName : attribute.N
//序列化请求消息
Message requestMessage = this.MessageFormatters[operationName].SerializeRequest(this.MessageVersion, methodCall.InArgs);
//添加必要的WS-Address报头
EndpointAddress address = new EndpointAddress(this.Address);
requestMessage.Headers.MessageId = new UniqueId(Guid.NewGuid());
requestMessage.Headers.ReplyTo = new EndpointAddress(&http://www.w3.org/2005/08/addressing/anonymous&);
address.ApplyTo(requestMessage);
//对请求消息进行编码,并将编码生成的字节发送通过HttpWebRequest向服务端发送
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(this.Address);
webRequest.Method = &Post&;
webRequest.KeepAlive = true;
webRequest.ContentType = &application/soap+ charset=utf-8&;
ArraySegment&byte& bytes = this.MessageEncoderFactory.Encoder.WriteMessage(requestMessage, int.MaxValue, BufferManager.CreateBufferManager(long.MaxValue, int.MaxValue));
webRequest.ContentLength = bytes.Array.L
webRequest.GetRequestStream().Write(bytes.Array, 0, bytes.Array.Length);
webRequest.GetRequestStream().Close();
WebResponse webResponse = webRequest.GetResponse();
//对HttpResponse进行解码生成回复消息.
Message responseMessage = this.MessageEncoderFactory.Encoder.ReadMessage(webResponse.GetResponseStream(), int.MaxValue);
//回复消息进行反列化生成相应的对象,并映射为方法调用的返回值或者ref/out参数
object[] allArgs = (object[])Array.CreateInstance(typeof(object),methodCall.ArgCount);
Array.Copy(methodCall.Args, allArgs, methodCall.ArgCount);
object[] refOutParameters = new object[GetRefOutParameterCount(methodCall.MethodBase)];
object returnValue = this.MessageFormatters[operationName].DeserializeReply(responseMessage, refOutParameters);
MapRefOutParameter(methodCall.MethodBase, allArgs, refOutParameters);
//通过ReturnMessage的形式将返回值和ref/out参数返回
return new ReturnMessage(returnValue, allArgs, allArgs.Length, methodCall.LogicalCallContext, methodCall);
private int GetRefOutParameterCount(MethodBase method)
int count = 0;
foreach (ParameterInfo parameter in method.GetParameters())
if (parameter.IsOut || parameter.ParameterType.IsByRef)
private void MapRefOutParameter(MethodBase method, object[] allArgs,object[] refOutArgs)
List&int& refOutParamPositionsList = new List&int&();
foreach (ParameterInfo parameter in method.GetParameters())
if (parameter.IsOut || parameter.ParameterType.IsByRef)
refOutParamPositionsList.Add(parameter.Position);
int[] refOutParamPositionArray = refOutParamPositionsList.ToArray();
for (int i = 0; i & refOutArgs.L i++)
allArgs[refOutParamPositionArray[i]] = refOutArgs[i];
六、定义服务代理工厂
WCF的服务代理对象是通过ChannelFactory&TChannel&创建的,我们来创建如下一个与之对应的ServiceProxyFactory&TChannel&类,泛型参数依然表示契约接口类型。CreateChannel方法中通过表示服务地址的Uri,契约接口类型和默认消息版本创建上述的真实代理ServiceChannelProxy&TChannel&对象,并返回其透明代理作为进行服务调用的代理对象。
1: public class ServiceProxyFactory&TChannel&
public Uri Address { private }
public ServiceProxyFactory(Uri address)
this.Address =
public TChannel CreateChannel()
MessageEncoderFactory encoderFactory = ComponentBuilder.GetMessageEncoderFactory(MessageVersion.Default, Encoding.UTF8);
ServiceChannelProxy&TChannel& proxy = new ServiceChannelProxy&TChannel&(this.Address, MessageVersion.Default, encoderFactory);
ContractDescription contract = ContractDescription.GetContract(typeof(TChannel));
foreach (OperationDescription operation in contract.Operations)
IClientMessageFormatter messageFormatter = (IClientMessageFormatter)ComponentBuilder.GetFormatter(operation, true);
proxy.MessageFormatters.Add(operation.Name, messageFormatter);
return (TChannel)proxy.GetTransparentProxy();
七、服务“寄宿”和调用
现在我们创建一个服务寄宿在我们自定义的迷你版本的WCF中。依然采用我们熟悉的计算服务,下面是分别定义的Service.Interface和Service项目中的契约接口定义和服务类型定义。
1: //契约接口
2: [ServiceContract(Namespace = &/&)]
3: public interface ICalculator
[OperationContract]
double Add(double x, double y);
[OperationContract]
double Subtract(double x, double y);
[OperationContract]
double Multiply(double x, double y);
[OperationContract]
double Divide(double x, double y);
15: //服务类型
16: public
class CalculatorService: ICalculator
public double Add(double x, double y)
return x +
public double Subtract(double x, double y)
return x -
public double Multiply(double x, double y)
return x *
public double Divide(double x, double y)
return x /
然后我们为Web项目Service中添加一个Global.asax文件,并通过如下的定义让Web应用启动的时候注册寄宿的服务类型CalculatorService和地址(calculatorservice)之间的映射关系。然后在IIS中创建一个Web应用(比如起名为WcfServices)并将物理路径映射为Service项目的根目录。
1: public class Global : System.Web.HttpApplication
protected void Application_Start(object sender, EventArgs e)
RouteTable.Routes.Register&CalculatorService&(&calculatorservice&);
由于最终处理服务调用请求的WcfHandler是通过WcfHttpModule进行映射的,所以我们需要将WcfHttpModule类型配置在Service项目的Web.config中。
1: &configuration&
&system.webServer&
&add name=&WcfHttpModule& type=&Artech.WcfServices.Service.WcfHttpModule, Artech.WcfServices.Service&/&
&/modules&
&/system.webServer&
7: &/configuration&
在客户端我们只需要按照如下的方式通过指定正确的调用地址(Web应用地址+在Global.asax文件中添加的路由映射的地址)创建ServiceProxyFactory&TChannel&对象,并用它来创建用于尽心服务调用的代理对象即可。
1: Uri address = new Uri(&http://localhost/WcfServices/CalculatorService&);
2: ServiceProxyFactory&ICalculator& factory = new ServiceProxyFactory&ICalculator&(address);
3: ICalculator proxy = factory.CreateChannel();
4: Console.WriteLine(&x + y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Add(1, 2));
5: Console.WriteLine(&x - y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Subtract(1, 2));
6: Console.WriteLine(&x * y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Multiply(1, 2));
7: Console.WriteLine(&x / y = {2} when x = {0} and y = {1}&, 1, 2, proxy.Divide(1, 2));
上面的代码执行之后,就像你真正调用WCF服务一样,同样可以得到如下的运算结果。
1: x + y = 3 when x = 1 and y = 2
2: x - y = -1 when x = 1 and y = 2
3: x * y = 2 when x = 1 and y = 2
4: x / y = 0.5 when x = 1 and y = 2
原文链接:}

我要回帖

更多关于 wcf 客户端代理类 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信