透過一個模擬程式讓你明白WCF大致的執行流程

mpsky發表於2021-09-09

在《透過一個模擬程式讓你明白ASP.NET MVC是如何執行的》一文中我透過一個普通的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

   

   2: {

   

   3:     //其他成員

   

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

   

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

   

   6:     public IDictionary Methods { get; private set; }

   

   7:     public IDictionary MessageFormatters { get; private set; }

   

   8:     public IDictionary 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();

   

  20:         this.MessageFormatters = new Dictionary();

   

  21:         this.OperationInvokers = new Dictionary();

   

  22:     }

   

  23: }

如上面程式碼所示,上述的關於WCF服務端框架所需的元件以只讀屬性的方式體現在WcfHandler上。ServiceType屬性表示服務的型別,基於這個型別透過反射建立服務例項。訊息編碼器工廠透過MessageEncoderFactory屬性表示,兩個字典型別的屬性MessageFormatters和OperationInvokers代表基於操作的分發訊息格式化器和操作呼叫器列表,字典的Key為操作請求訊息的報頭的值。而Methods表示契約介面所有操作方法的MethodInfo集合。

針對WCF服務的請求處理實現在如下的ProcessRequest方法中,執行的邏輯也不算複雜。我們直接透過訊息編碼器工廠建立的訊息編碼從當前HTTP請求的輸入流中讀取出訊息。然後根據當前訊息的報頭的值從MessageFormatters屬性中找到與當前請求操作相匹配的分發訊息格式化器對訊息進行反序列化。

接著直接透過反射的方式根據服務型別建立服務例項物件。同樣根據當前訊息的報頭從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 inputArgs = new List();

   

  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。

   1: public class WcfHttpModule: IHttpModule

   

   2: {

   

   3:     public void Dispose() {}

   

   4:

   

   5:     public void Init(HttpApplication context)

   

   6:     {

   

   7:         context.BeginRequest += (sender, args) =>

   

   8:             {

   

   9:                 string relativeAddress = HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.Remove(0,2);

   

  10:                 Type serviceType = RouteTable.Routes.Find(relativeAddress);

   

  11:                 if (null == serviceType)

   

  12:                 {

   

  13:                     return;

   

  14:                 }

   

  15:                 IHttpHandler handler = this.CreateHttpHandler(serviceType);

   

  16:                 context.Context.RemapHandler(handler);

   

  17:             };

   

  18:     }

   

  19:     protected IHttpHandler CreateHttpHandler(Type serviceType)

   

  20:     {

   

  21:         MessageEncoderFactory encoderFactory = ComponentBuilder.GetMessageEncoderFactory(MessageVersion.Default, Encoding.UTF8);

   

  22:         WcfHandler handler = new WcfHandler(serviceType, encoderFactory);

   

  23:         Type interfaceType = serviceType.GetInterfaces()[0];

   

  24:         ContractDescription contract = ContractDescription.GetContract(interfaceType);

   

  25:         foreach (OperationDescription operation in contract.Operations)

   

  26:         {

   

  27:             IDispatchMessageFormatter messageFormatter = (IDispatchMessageFormatter)ComponentBuilder.GetFormatter(operation, false);

   

  28:             handler.MessageFormatters.Add(operation.Messages[0].Action, messageFormatter);

   

  29:

   

  30:             IOperationInvoker operationInvoker = ComponentBuilder.GetOperationInvoker(operation.SyncMethod);

   

  31:             handler.OperationInvokers.Add(operation.Messages[0].Action, operationInvoker);

   

  32:

   

  33:             handler.Methods.Add(operation.Messages[0].Action, operation.SyncMethod);

   

  34:         }

   

  35:         return handler;

   

  36:     }

   

  37: }

至於WcfHandler的建立,需要確定服務的型別。而服務的型別只能根據請求的地址來確定,這個IIS寄宿根據.svc檔案來建立ServiceHost的原理是一樣的。對於本例來說,我們需要對請求的地址和服務型別作一個對映,為此我們定義瞭如下一個RouteMapping的型別表示這個對映。

   1: public class RouteMapping

   

   2: {

   

   3:     public string Address { get; private set; }

   

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

   

   5:     public RouteMapping(string address, Type serviceType)

   

   6:     {

   

   7:         this.Address = address;

   

   8:         this.ServiceType = serviceType;

   

   9:     }

   

  10: }

而對映表則透過如下一個繼承自Collection的RouteTable來定義。泛型的Register方法用於註冊地址與服務型別的對映關係,而Find方法則根據地址獲取相應的服務型別。靜態屬性Routes表示當前被使用的對映表,而在WcfHttpModule中正是透過這個靜態屬性根據解析出來的地址得到用於建立WcfHandler的服務型別的。

   1: public class RouteTable: Collection

   

   2: {

   

   3:     public static RouteTable Routes{get; private set;}

   

   4:     static RouteTable()

   

   5:     {

   

   6:         Routes = new RouteTable();

   

   7:     }

   

   8:     public Type Find(string address)

   

   9:     {

   

  10:         RouteMapping routeMapping =  (from route in this

   

  11:                 where string.Compare(route.Address, address,true) == 0

   

  12:                 select route).FirstOrDefault();

   

  13:         return null == routeMapping? null: routeMapping.ServiceType;

   

  14:     }

   

  15:

   

  16:     public void Register(string address)

   

  17:     {

   

  18:         this.Add(new RouteMapping(address, typeof(T)));

   

  19:     }

   

  20: }


五、建立自定義的真實代理實現服務的呼叫

ChannelFactory建立的服務代理僅僅是一個透明代理,而真實實現服務呼叫的是它的真實代理。為此我們建立瞭如下一個繼承自RealProxy的泛型的ServiceChannelProxy,其中泛型引數為契約介面型別。

   1: public class ServiceChannelProxy: RealProxy

   

   2: {

   

   3:     //其他成員

   

   4:     public Uri Address { get; private set; }

   

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

   

   6:     public IDictionary 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();

   

  15:     }

   

  16: }

和WcfHttpHandler類似,進行服務呼叫所需的元件透過相應的只讀屬性表示。屬性MessageEncoderFactory表示訊息編碼器工廠,而字典型別的MessageFormatters表示基於每個操作的客戶端訊息格式化器列表,其中的Key為操作的名稱。屬性Address表示被呼叫服務的地址。

針對透明代理的方法呼叫最終都會轉移到針對真實真實代理的Invoke方法,所以我們將所有的服務呼叫操作實現在如下的Invoke方法中。我們首先獲取代表當前呼叫方法的MethodBase上應用的OperationContractAttribute特性,並藉此獲得操作名稱。

根據獲取的操作名稱從屬性MessageFormatters屬性中獲得基於當前操作的客戶端訊息格式化器,並將方法呼叫轉化訊息。接著根據Address屬性表示的服務呼叫地址建立EndpointAddress物件並將其附加到請求訊息中。除此之外,還需要為請求訊息新增一些必要的報頭(比如)。

接下來透過訊息編碼器工廠建立的訊息編碼器對訊息進行編碼,並將得到的位元組資料透過建立的HttpWebRequest物件傳送出去。對於得到的HttpWebResponse,則透過訊息編碼器進行解碼以生成回覆訊息。回覆訊息最終透過客戶端訊息格式化器進行反序列化,得到的物件對映為方法返回值和輸出/引用引數返回。

   1: public class ServiceChannelProxy: RealProxy

   

   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("");

   

  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 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 refOutParamPositionsList = new List();

   

  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 

   

  72:         {

   

  73:             allArgs[refOutParamPositionArray[i]] = refOutArgs[i];

   

  74:         }

   

  75:     }

   

  76: }


六、定義服務代理工廠

WCF的服務代理物件是透過ChannelFactory建立的,我們來建立如下一個與之對應的ServiceProxyFactory類,泛型引數依然表示契約介面型別。CreateChannel方法中透過表示服務地址的Uri,契約介面型別和預設訊息版本建立上述的真實代理ServiceChannelProxy物件,並返回其透明代理作為進行服務呼叫的代理物件。

   1: public class ServiceProxyFactory

   

   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 proxy = new ServiceChannelProxy(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 = "")]

   

   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");

   

   6:     }

   

   7: }

由於最終處理服務呼叫請求的WcfHandler是透過WcfHttpModule進行對映的,所以我們需要將WcfHttpModule型別配置在Service專案的Web.config中。

   1: 

   

   2:   

   

   3:     

   

   4:       

   

   5:     

   

   6:   

   

   7: 

在客戶端我們只需要按照如下的方式透過指定正確的呼叫地址(Web應用地址+在Global.asax檔案中新增的路由對映的地址)建立ServiceProxyFactory物件,並用它來建立用於盡心服務呼叫的代理物件即可。

   1: Uri address = new Uri("http://localhost/WcfServices/CalculatorService");

   

   2: ServiceProxyFactory factory = new ServiceProxyFactory(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

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1806/viewspace-2806148/,如需轉載,請註明出處,否則將追究法律責任。

相關文章