现在的位置: 首页 > 综合 > 正文

通过一个模拟程序让你明白WCF大致的执行流程

2012年09月20日 ⁄ 综合 ⁄ 共 6203字 ⁄ 字号 评论关闭

在《通过一个模拟程序让你明白ASP.NET MVC是如何运行的》一文中我通过一个普通的ASP.NET Web程序模拟了ASP.NET MVC的执行流程,现在我们通过类似的原理创建一个用于模拟WCF服务端和客户端工作原理的模拟程序。[源代码从这里下载]

目录
一、基本的组件和执行流程
二、创建自定义HttpHandler实现对服务调用请求的处理
三、定义创建WCF组件的工厂
四、定义HttpModule映射WcfHandler
五、创建自定义的真实代理实现服务的调用
六、定义服务代理工厂
七、服务“寄宿”和调用

一、基本的组件和执行流程

clip_image002我们只模拟WCF完成一个简单的服务调用所必需的组件和流程,右图反映了进行服务调用的必要步骤和使用的相关WCF组件。下面列出了服务端涉及的组件和流程:

  • 请求消息的接收和回复消息的发送:服务端在传输层监听与接收来自客户的请求,并将经过编码后的回复消息通过传输层发送到客户端;
  • 请求消息的解码和回复消息的编码:将接收到的字节数组通过解码生成请求消息对象,并将回复消息通过编码转化成字节数组。消息的编码和解码通过消息编码器(MessageEncoder)完成,而消息编码器工厂(MessageEncoderFactory)负责创建该对象;
  • 请求消息的反序列化和回复消息的序列化:对请求消息进行反序列化,为服务操作的执行生成相应的输入参数,以及将服务操作执行的结果(返回值或输出/引用参数)序列化,并生成回复消息。序列化和反序列化通过分发消息格式化器(DispatchMessageFormatter)完成;
  • 服务对象的创建:创建或激活服务对象实例,实例提供者(InstanceProvider)用于服务对象的创建或获取,本例直接通过反射创建服务实例;
  • 服务操作的执行:调用创建的服务对象的操作方法,并传入经过反序列化生成的输入参数。操作调用器(OperationInvoker)完成对服务操作的最终执行。

相较于服务端的请求监听、消息接收、服务实例激活和操作调用流程,客户端的处理流程显得相对简单,仅仅包含以下3个必需的步骤:

  • image请求消息的序列化和回复消息的反序列化:生成请求消息并将输入参数序列化到请求消息中,以及对回复消息进行反序列化,转化成方法调用的返回值或输出/引用参数。序列化和反序列化通过ClientMessageFormatter完成;
  • 请求消息的编码和回复消息的解码:对请求消息进行编码生成字节数组供传输层发送,以及将传输层接收到的字节数组解码生成回复消息。消息的编码和解码通过消息编码器完成,而消息编码器工厂负责创建该对象;
  • 请求消息的发送和回复消息的接收:在传输层将经过编码的请求消息发送到服务端,以及接收来自服务端的回复消息。

本实例的解决方法依然采用包含Service.Interface、Service和Client三个项目的结构,不过Service项目现在是一个Web应用。也就是说我们通过一个Web应用的方式实现WCF端对服务调用请求的整个处理流程。

二、创建自定义HttpHandler实现对服务调用请求的处理

对于一个ASP.NET Web应用来说,对请求的处理最终都落实到一个具体的HttpHandler对象上,所以我们通过实现接口System.Web.IHttpHandler自定义了如下一个WcfHandler用于处理针对WCF服务请求的处理。

   1: public class WcfHandler: IHttpHandler

   2: {

   3:     //其他成员

   4:     public Type ServiceType { get; private set; }

   5:     public MessageEncoderFactory MessageEncoderFactory { get; private set; }

   6:     public IDictionary<string, MethodInfo> Methods { get; private set; }

   7:     public IDictionary<string, IDispatchMessageFormatter> MessageFormatters { get; private set; }

   8:     public IDictionary<string, IOperationInvoker> OperationInvokers { get; private set; } 

   9:  

  10:     public bool IsReusable

  11:     {

  12:         get { return false; }

  13:     }

  14:  

  15:     public WcfHandler(Type serviceType, MessageEncoderFactory messageEncoderFactory)

  16:     {

  17:         this.ServiceType = serviceType;

  18:         this.MessageEncoderFactory = messageEncoderFactory;

  19:         this.Methods = new Dictionary<string, MethodInfo>();

  20:         this.MessageFormatters = new Dictionary<string, IDispatchMessageFormatter>();

  21:         this.OperationInvokers = new Dictionary<string, IOperationInvoker>();            

  22:     }

  23: }

如上面代码所示,上述的关于WCF服务端框架所需的组件以只读属性的方式体现在WcfHandler上。ServiceType属性表示服务的类型,基于这个类型通过反射创建服务实例。消息编码器工厂通过MessageEncoderFactory属性表示,两个字典类型的属性MessageFormatters和OperationInvokers代表基于操作的分发消息格式化器和操作调用器列表,字典的Key为操作请求消息的<Action>报头的值。而Methods表示契约接口所有操作方法的MethodInfo集合。

针对WCF服务的请求处理实现在如下的ProcessRequest方法中,执行的逻辑也不算复杂。我们直接通过消息编码器工厂创建的消息编码从当前HTTP请求的输入流中读取出消息。然后根据当前消息的<Action>报头的值从MessageFormatters属性中找到与当前请求操作相匹配的分发消息格式化器对消息进行反序列化。

接着直接通过反射的方式根据服务类型创建服务实例对象。同样根据当前消息的<Action>报头从OperationInvokers属性获取出基于当前请求操作的操作调用器,并将创建的服务实例和反序列化后生成的参数作为输入执行操作方法。

操作的执行结果通过分发消息格式化器进行序列化生成的消息最终通过消息编码器写入当前HTTP回复的输出流中返回给客户端。

   1: public class WcfHandler: IHttpHandler

   2: {

   3:     //其他成员

   4:     public void ProcessRequest(HttpContext context)

   5:     {

   6:         //对HttpPRequest进行解码生成请求消息对象

   7:         Message request = this.MessageEncoderFactory.Encoder.ReadMessage(context.Request.InputStream, int.MaxValue, "application/soap+xml; charset=utf-8");

   8:  

   9:         //通过请求消息得到代表服务操作的Action

  10:         string action = request.Headers.Action;

  11:  

  12:         //通过Action从MethodInfo字典中获取服务操作对应的MethodInfo对象

  13:         MethodInfo method = this.Methods[action];

  14:  

  15:         //得到输出参数的数量

  16:         int outArgsCount = 0;

  17:         foreach (var parameter in method.GetParameters())

  18:         {

  19:             if (parameter.IsOut)

  20:             {

  21:                 outArgsCount++;

  22:             }

  23:         }

  24:  

  25:         //创建数组容器,用于保存请求消息反序列后生成的输入参数对象

  26:         int inputArgsCount = method.GetParameters().Length - outArgsCount;

  27:         object[] parameters = new object[inputArgsCount];

  28:         try

  29:         {

  30:             this.MessageFormatters[action].DeserializeRequest(request, parameters);

  31:         }

  32:         catch

  33:         {}

  34:  

  35:         List<object> inputArgs = new List<object>();

  36:         object[] outArgs = new object[outArgsCount];

  37:         //创建服务对象,在WCF中服务对象通过InstanceProvider创建

  38:  

  39:         object serviceInstance = Activator.CreateInstance(this.ServiceType);

  40:  

  41:         //执行服务操作

  42:         object result = this.OperationInvokers[action].Invoke(serviceInstance,parameters, out outArgs);

  43:  

  44:         //将操作执行的结果(返回值或者输出参数)序列化生成回复消息

  45:         Message reply = this.MessageFormatters[action].SerializeReply(request.Version, outArgs, result);

  46:         context.Response.ClearContent();

  47:         context.Response.ContentEncoding = Encoding.UTF8;

  48:         context.Response.ContentType = "application/soap+xml; charset=utf-8";

  49:  

  50:         //对回复消息进行编码,并将编码后的消息通过HttpResponse返回

  51:         this.MessageEncoderFactory.Encoder.WriteMessage(reply, context.Response.OutputStream);

  52:         context.Response.Flush();

  53:     }

  54: }

三、定义创建WCF组件的工厂

对于本例来说,客户端和服务端需要的组件主要有四类,即消息编码器工厂、分发消息格式化器、客户端消息格式化器和操作调用器。我们通过具有如下定义的静态的工厂类ComponentBuilder来创建它们。我们调用操作行为DataContractSerializerOperationBehavior的GetFormatter方法来创建基于指定操作的消息格式化器。不过该方法是一个内部方法,所以我们是通过反射的方式来调用的。isProxy参数表示创建的是客户端消息格式化器(True)还是分发消息格式化器(False)。

消息编码器工厂通过基于文本编码方式绑定元素TextMessageEncodingBindingElement的CreateMessageEncoderFactory创建,传入的参数分别表示消息的版本和文本编码类型。我们采用SyncMethodInvoker以同步的方式进行操作的执行。由于SyncMethodInvoker是一个内部类型,所以我们不得不采用反射的方式来创建它。

   1: public static class ComponentBuilder

   2: {

   3:     public static object GetFormatter(OperationDescription operation, bool isProxy)

   4:     {

   5:         bool formatRequest = false;

   6:         bool formatReply = false;

   7:         DataContractSerializerOperationBehavior behavior = new DataContractSerializerOperationBehavior(operation);

   8:         MethodInfo method = typeof(DataContractSerializerOperationBehavior).GetMethod("GetFormatter", BindingFlags.Instance | BindingFlags.NonPublic);

   9:         return method.Invoke(behavior, new object[] { operation, formatRequest, formatReply, isProxy });

  10:     }

  11:  

  12:     public static MessageEncoderFactory GetMessageEncoderFactory(MessageVersion messageVersion, Encoding writeEncoding)

  13:     {

  14:         TextMessageEncodingBindingElement bindingElement = new TextMessageEncodingBindingElement(messageVersion, writeEncoding);

  15:         return bindingElement.CreateMessageEncoderFactory();

  16:     }

  17:     public static IOperationInvoker GetOperationInvoker(MethodInfo method)

  18:     {

  19:         string syncMethodInvokerType = "System.ServiceModel.Dispatcher.SyncMethodInvoker, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

  20:         Type type = Type.GetType(syncMethodInvokerType);

  21:         return (IOperationInvoker)Activator.CreateInstance(type, new object[]{method});

  22:     }

  23: }

四、定义HttpModule映射WcfHandler

我们通过HttpModule的方式将用于处理WCF服务请求的映射到相应的WCF服务调用请求,为此我们定义了如下一个实现了System.Web.IHttpModule接口的WcfHttpModule类型。WcfHttpModule通过注册HttpApplication的BeginRequest事件的方式将创建的WcfHandler映射为处理当前HTTP请求的HttpHandler。

抱歉!评论已关闭.