技术开发 频道

通过模拟程序 解析WCF大致的执行流程

  创建自定义的真实代理实现服务的调用

  ChannelFactory创建的服务代理仅仅是一个透明代理,而真实实现服务调用的是它的真实代理。为此我们创建了如下一个继承自RealProxy的泛型的ServiceChannelProxy,其中泛型参数为契约接口类型。

   1: public class ServiceChannelProxy<TChannel>: RealProxy   2: {
  
3:     //其他成员
  
4:     public Uri Address { get; private set; }
  
5:     public MessageVersion MessageVersion { get; private set;
}  
6:     public IDictionary<string, IClientMessageFormatter> MessageFormatters { get; private set; }
  
7:     public MessageEncoderFactory MessageEncoderFactory { get; private set; }
  
8:
  
9:     public ServiceChannelProxy(Uri address, MessageVersion messageVersion, MessageEncoderFactory encoderFactory): base(typeof(TChannel))
  
10:    
{
  
11:         this.Address = address;
  
12:         this.MessageVersion = messageVersion;
  
13:         this.MessageEncoderFactory = encoderFactory;
  
14:         this.MessageFormatters = new Dictionary<string, IClientMessageFormatter>();
  
15:     }
  
16: }

   和WcfHttpHandler类似,进行服务调用所需的组件通过相应的只读属性表示。属性MessageEncoderFactory表示消息编码器工厂,而字典类型的MessageFormatters表示基于每个操作的客户端消息格式化器列表,其中的Key为操作的名称。属性Address表示被调用服务的地址。

  针对透明代理的方法调用最终都会转移到针对真实真实代理的Invoke方法,所以我们将所有的服务调用操作实现在如下的Invoke方法中。我们首先获取代表当前调用方法的MethodBase上应用的OperationContractAttribute特性,并借此获得操作名称。

  根据获取的操作名称从属性MessageFormatters属性中获得基于当前操作的客户端消息格式化器,并将方法调用转化消息。接着根据Address属性表示的服务调用地址创建EndpointAddress对象并将其附加到请求消息中。除此之外,还需要为请求消息添加一些必要的报头(比如)。

  接下来通过消息编码器工厂创建的消息编码器对消息进行编码,并将得到的字节数据通过创建的HttpWebRequest对象发送出去。对于得到的HttpWebResponse,则通过消息编码器进行解码以生成回复消息。回复消息最终通过客户端消息格式化器进行反序列化,得到的对象映射为方法返回值和输出/引用参数返回。

   1: public class ServiceChannelProxy<TChannel>: RealProx
  
2: {
  
3:     //其他成
  
4:     public override IMessage Invoke(IMessage msg)
  
5:     {
  
6:         IMethodCallMessage methodCall = (IMethodCallMessage)msg;
  
7:          
  
8:         //得到操作名称
  
9:         object[] attributes = methodCall.MethodBase.GetCustomAttributes(typeof(OperationContractAttribute), true);
  
10:         OperationContractAttribute attribute = (OperationContractAttribute)attributes[0];
  
11:         string operationName = string.IsNullOrEmpty(attribute.Name) ? methodCall.MethodName : attribute.Name;
  
12:            
  
13:         //序列化请求消息
  
14:         Message requestMessage = this.MessageFormatters[operationName].SerializeRequest(this.MessageVersion, methodCall.InArgs);
  
15:
  
16:         //添加必要的WS-Address报头
  
17:         EndpointAddress address = new EndpointAddress(this.Address);
  
18:         requestMessage.Headers.MessageId = new UniqueId(Guid.NewGuid());
  
19:         requestMessage.Headers.ReplyTo = new EndpointAddress("http://www.w3.org/2005/08/addressing/anonymous");
  
20:         address.ApplyTo(requestMessage);
  
21:
  
22:         //对请求消息进行编码,并将编码生成的字节发送通过HttpWebRequest向服务端发送
  
23:         HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(this.Address);
  
24:         webRequest.Method = "Post";
  
25:         webRequest.KeepAlive = true;
  
26:         webRequest.ContentType = "application/soap+xml; charset=utf-8";
  
27:         ArraySegment<byte> bytes = this.MessageEncoderFactory.Encoder.WriteMessage(requestMessage, int.MaxValue, BufferManager.CreateBufferManager(long.MaxValue, int.MaxValue));
  
28:         webRequest.ContentLength = bytes.Array.Length;
  
29:         webRequest.GetRequestStream().Write(bytes.Array, 0, bytes.Array.Length);
  
30:         webRequest.GetRequestStream().Close();
  
31:         WebResponse webResponse = webRequest.GetResponse();
  
32:
  
33:         //对HttpResponse进行解码生成回复消息.
  
34:         Message responseMessage = this.MessageEncoderFactory.Encoder.ReadMessage(webResponse.GetResponseStream(), int.MaxValue);
  
35:            
  
36:         //回复消息进行反列化生成相应的对象,并映射为方法调用的返回值或者ref/out参数
  
37:         object[] allArgs = (object[])Array.CreateInstance(typeof(object),methodCall.ArgCount);
  
38:         Array.Copy(methodCall.Args, allArgs, methodCall.ArgCount);
  
39:         object[] refOutParameters = new object[GetRefOutParameterCount(methodCall.MethodBase)];
  
40:         object returnValue = this.MessageFormatters[operationName].DeserializeReply(responseMessage, refOutParameters);
  
41:         MapRefOutParameter(methodCall.MethodBase, allArgs, refOutParameters);
  
42:
  
43:         //通过ReturnMessage的形式将返回值和ref/out参数返回
  
44:         return new ReturnMessage(returnValue, allArgs, allArgs.Length, methodCall.LogicalCallContext, methodCall);
  
45:     }
  
46:  
  
47:     private int GetRefOutParameterCount(MethodBase method)
  
48:     {
  
49:         int count = 0;
  
50:         foreach (ParameterInfo parameter in method.GetParameters())
  
51:         {
  
52:             if (parameter.IsOut || parameter.ParameterType.IsByRef)
  
53:             {
  
54:                 count++;
  
55:             }
  
56:         }
  
57:         return count;
  
58:     }
  
59:
  
60:     private void MapRefOutParameter(MethodBase method, object[] allArgs,object[] refOutArgs)
  
61:     {
  
62:         List<int> refOutParamPositionsList = new List<int>();
  
63:         foreach (ParameterInfo parameter in method.GetParameters())
  
64:         {
  
65:             if (parameter.IsOut || parameter.ParameterType.IsByRef)
  
66:             {
  
67:                 refOutParamPositionsList.Add(parameter.Position);
  
68:             }
  
69:         }
  
70:         int[] refOutParamPositionArray = refOutParamPositionsList.ToArray();
  
71:         for (int i = 0; i < refOutArgs.Length; i++)
  
72:         {
  
73:             allArgs[refOutParamPositionArray[i]] = refOutArgs[i];
  
74:         }
  
75:     }
  
76: }

   定义服务代理工厂

  WCF的服务代理对象是通过ChannelFactory创建的,我们来创建如下一个与之对应的ServiceProxyFactory类,泛型参数依然表示契约接口类型。CreateChannel方法中通过表示服务地址的Uri,契约接口类型和默认消息版本创建上述的真实代理ServiceChannelProxy对象,并返回其透明代理作为进行服务调用的代理对象。

   1: public class ServiceProxyFactory<TChannel>
  
2: {
  
3:     public Uri Address { get; private set; }
  
4:        
  
5:     public ServiceProxyFactory(Uri address)
  
6:     {
  
7:         this.Address = address;
  
8:     }
  
9:     public TChannel CreateChannel()
  
10:     {
  
11:         MessageEncoderFactory encoderFactory = ComponentBuilder.GetMessageEncoderFactory(MessageVersion.Default, Encoding.UTF8);
  
12:         ServiceChannelProxy<TChannel> proxy = new ServiceChannelProxy<TChannel>(this.Address, MessageVersion.Default, encoderFactory);
  
13:         ContractDescription contract = ContractDescription.GetContract(typeof(TChannel));
  
14:         foreach (OperationDescription operation in contract.Operations)
  
15:         {
  
16:             IClientMessageFormatter messageFormatter = (IClientMessageFormatter)ComponentBuilder.GetFormatter(operation, true);
  
17:             proxy.MessageFormatters.Add(operation.Name, messageFormatter);
  
18:         }
  
19:         return (TChannel)proxy.GetTransparentProxy();
  
20:     }
  
21: }

   服务“寄宿”和调用

  现在我们创建一个服务寄宿在我们自定义的迷你版本的WCF中。依然采用我们熟悉的计算服务,下面是分别定义的Service.Interface和Service项目中的契约接口定义和服务类型定义。

   1: //契约接口
  
2: [ServiceContract(Namespace = "http://www.artech.com/")]   3: public interface ICalculator
  
4: {
  
5:     [OperationContract]
  
6:     double Add(double x, double y);
  
7:     [OperationContract]
  
8:     double Subtract(double x, double y);
  
9:     [OperationContract]
  
10:     double Multiply(double x, double y);
  
11:     [OperationContract]
  
12:     double Divide(double x, double y);  13: }
  
14:
  
15: //服务类型
  
16: public  class CalculatorService: ICalculator
  
17: {
  
18:     public double Add(double x, double y)
  
19:     {
  
20:         return x + y;
  
21:     }
  
22:     public double Subtract(double x, double y)
  
23:     {
  
24:         return x - y;
  
25:     }
  
26:     public double Multiply(double x, double y)
  
27:     {
  
28:         return x * y;
  
29:     }
  
30:     public double Divide(double x, double y)
  
31:     {
  
32:         return x / y;
  
33:     }
  
34: }

   然后我们为Web项目Service中添加一个Global.asax文件,并通过如下的定义让Web应用启动的时候注册寄宿的服务类型CalculatorService和地址(calculatorservice)之间的映射关系。然后在IIS中创建一个Web应用(比如起名为WcfServices)并将物理路径映射为Service项目的根目录。

   1: public class Global : System.Web.HttpApplication
  
2: {
  
3:     protected void Application_Start(object sender, EventArgs e)
  
4:     {
  
5:         RouteTable.Routes.Register<CalculatorService>("calculatorservice");
  
6:     }
  
7: }

  由于最终处理服务调用请求的WcfHandler是通过WcfHttpModule进行映射的,所以我们需要将WcfHttpModule类型配置在Service项目的Web.config中

   1: <configuration>
  
2:   <system.webServer>
  
3:     <modules>
  
4:       <add name="WcfHttpModule" type="Artech.WcfServices.Service.WcfHttpModule, Artech.WcfServices.Service"/>
  
5:     </modules>
  
6:   </system.webServer>
  
7: </configuration>

  在客户端我们只需要按照如下的方式通过指定正确的调用地址(Web应用地址+在Global.asax文件中添加的路由映射的地址)创建ServiceProxyFactory对象,并用它来创建用于尽心服务调用的代理对象即可。

   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

        源代码下载:http://files.cnblogs.com/artech/wcf-how-to-work.rar
        出处:http://artech.cnblogs.com
 

1
相关文章