WCF之各種WCF引用方式


寫在開頭:本文內容來自 WCF全面解析中的一個經典例子,如果你已經看過了,那么可以忽略本文,本文旨在和大家分享不一樣的WCF使用方法。

准備工作:

1.創建解決方案WCFService(當然名字可以任意哦)

依次添加四個項目,如上圖,Client和Hosting為控制台應用程序,Service和Service.Interface均為類庫。

2.引用關系

Service.Interface:定義服務契約(Service Contract)接口,引用WCF核心庫System.ServiceModel.dll;

Service:定義服務的項目,由於需要實現具體的服務,而服務契約在Service.Interface中,所以要引用Service.Interface項目;

Hosting:服務宿主的控制台程序,需要引用Service.Interface和Service項目,同時還要引用System.ServiceModel.dll類庫:

Client:一個控制台應用程序的客戶端,需要引用Service.ServiceModel類庫。

Service.Interface:

服務契約抽象了服務的所有操作,一般契約為接口形式存在。

    [ServiceContract(Name = "CalculatorService", Namespace = "Listen.Fly")]
    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);
    }

通過使用System.ServiceModel.ContractAttribute特性來標識接口為服務契約,同時可以在特性中指定契約的Name和Namespace。通過ContractAttribute特性標識為契約后的接口的方法並不能自動成為服務操作,WCF采用的是顯示選擇策略,所以我們要在對應的服務操作上添加OperationContractAttribute特性。

Service:

在Service項目中添加類CalculatorService類,並實現自ICalculator接口,並將服務操作(加減乘除四個方法)代碼補全。

   public class CalculatorService : ICalculator
    {
        public double Add(double x, double y)
        {
            return x + y;
        }

        public double Subtract(double x, double y)
        {
            return x - y;
        }

        public double Multiply(double x, double y)
        {
            return x * y;
        }

        public double Divide(double x, double y)
        {
            return x / y;
        }
    }

 Hosting:

WCF服務需要一個運行着的宿主進程,服務寄宿就是給服務指定一個宿主的過程。WCF采用基於終結點(EndPoint)的通信手段。終結點有地址(Address),綁定(Binding)和契約(Contract)三部分組成,三要素也可以記作:EndPoint=ABC。

一個終結點包含了通信所必須的所有信息,具體如下:
Address:地址決定了服務的位置,解決了尋址的問題;
Binding:綁定實現了通信的所有細節,包括網絡傳輸,消息編碼,以及其他為實現某種功能(比如傳輸安全,可靠消息傳輸,事務等)對消息進行的相應處理。WCF中具有一系列的系統定義綁定,比如BasicHttpBinding,WSHttpBinding和NetTcpBinding等;
Contract:契約是對服務操作的抽象,也是對消息交換模式以及消息結構的定義。

服務寄宿的目的是開啟一個進程,為WCF服務提供一個運行環境,並為服務添加一個或者多個終結點,然后暴漏給服務消費者。

Hosting的代碼如下:

            using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
            {
                host.AddServiceEndpoint(
                    typeof(ICalculator),
                    new WSHttpBinding(),
                    "http://127.0.0.1:1111/CalculatorService");
                if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
                {
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = new Uri("http://127.0.0.1:1111/CalculatorService/metadata");
                    host.Description.Behaviors.Add(behavior);
                }
                host.Opened += delegate
                    {
                        Console.Write("CalculatorService已經啟動,按任意鍵終止服務");
                    };
                host.Open();
                Console.Read();
            }

  WCF的寄宿通過System.ServiceModel.ServiceHost對象來完成,我們代碼中基於服務類型(typeof(CalculatorService))創建了ServiceHost對象。添加了基於WSHttpBinding綁定的終結點,服務契約類型為typeof(ICalculator),地址為"http://127.0.0.1:1111/CalculatorService"(此處的地址可以隨意指定)。
  WCF中客戶端和服務端是松耦合的,客戶端只需要知道WCF服務的基本描述,而不需知道服務的具體實現,就可以完成調用。WCF服務的描述通過元數據(Metadata)的形式發布出來,WCF中的元數據通過一個特殊的服務行為ServiceMetadataBehavior來實現。
  在上述代碼中我們為ServiceHost添加了ServiceMetadataBehavior這樣一個服務行為,並采用基於HTTP-GET的元數據獲取方式,並且通過ServiceMetadataBehavior的HttpGetUrl屬性指定元數據的發布地址(當前地址為http://127.0.0.1:1111/CalculatorService/metadata)。在服務啟動之后,訪問該地址可以看到元數據,即為XML返回數據的頁面。(也就是和我們通常點擊svc文件中的鏈接看到的結果是一樣的)

上面的代碼實在是不太好記住,不過通常我們也不會這么做,可以將這些配置的過程放在config文件中去:

<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataBehavior">
          <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:1111/CalculatorService/metadata"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="Service.CalculatorService" behaviorConfiguration="metadataBehavior">
        <endpoint address="http://127.0.0.1:1111/CalculatorService" binding="wsHttpBinding" 
                            contract="Service.Interface.ICalculator"></endpoint>
      </service>
    </services>
  </system.serviceModel>
</configuration>

打開Hosting項目中的app.config,添加上述代碼即可。xml中的標簽的意思和我們創建ServiceHost過程基本是一致的。

如果使用了配置文件的方式,我們的代碼可以更簡單,Hosting代碼修改如下:

            using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
            {
                host.Opened += delegate
            {
              Console.Write("CalculatorService已經啟動,按任意鍵終止服務");
            };
                host.Open();
                Console.Read();
            }

Client:

契約也定義了,服務也實現了,宿主也寫好了,還剩下最后一步調用服務,運行Hosting.exe(debug目錄下的哦,如果運行失敗,那么請用管理員身份運行),然后看到如下界面:

然后在Client中添加對服務的引用:

在地址中輸入我們配置的url,單擊Go即可找到發布的WCF(必須要一直運行着Hosting.exe),可以在Namespace修改為自定義的命名空間,點擊OK即可成功添加WCF。添加成功之后,被客戶端調用的契約接口CalculatorService會被生成出來。客戶端之所以會被命名為CalculatorService,是因為我們在ICalculator接口的特性中設置Name為CalculatorService。CalculatorService是與定義在Service.Interface項目中ICalculator接口等效的契約接口。但是我們在客戶端使用則是一個CalculatorServiceClient(當然不同的WCF名稱不一樣,但是都是以Client結尾),CalculatorServiceClient的基類為System.ServiceModel.ClientBase<CalculatorService>。CalculatorServiceClient同樣實現了契約接口CalculatorService,並通過從基類繼承的Channel屬性調用相對應的方法。

使用如下:

            using (CalculatorServiceClient proxy = new CalculatorServiceClient())
            {
                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}", 10, 2, proxy.Subtract(10, 2));
                Console.WriteLine("x*y={2} when x={0} and y={1}", 10, 2, proxy.Multiply(10, 2));
                Console.WriteLine("x/y={2} when x={0} and y={1}", 10, 2, proxy.Divide(10, 2));
                Console.Read();
            }

很簡單吧,只需定義一個xxClient對象,然后調用其對應的方法即可,當然我們現在使用的是同步的,每個方法都有對應的異步方法。比如Add對應的異步方法就是AddAsync,如果使用異步方法則要給xxClient添加對應的回調函數。
      戶端通過服務代理對象進行服務調用,上述代碼中的通過添加服務引用自動創建生成的、繼承自ClientBase<TChannel>的類型對象進行服務調用。實際上還有另外一種實現方法,通過System.ServiceModel.ChannelFactory<TChannel>直接創建服務代理對象。WCF采用基於契約的服務調用方法,從上述代碼中可以看到,VS在添加服務引用的過程中,會在客戶端創建一個與服務等效的服務契約接口。由於服務端和客戶端都在同一個解決方案,因此可以讓服務端和客戶端引用相同的契約。

客戶端使用第二種方法也就是ChannelFactory<T>形式創建代理對象,刪除掉之前添加的WCF的引用,然后添加對Service.Interface項目的引用(注:如果客戶端是Silverlight應用程序可能會提示無法添加,因為Silverlight應用程序只能添加Silverlight應用程序或者Silverlight類庫),然后修改代碼如下:

            using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(
                new WSHttpBinding(),
                "http://127.0.0.1:1111/CalculatorService"))
            {
                ICalculator proxy = channelFactory.CreateChannel();
                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}", 10, 2, proxy.Subtract(10, 2));
                Console.WriteLine("x*y={2} when x={0} and y={1}", 10, 2, proxy.Multiply(10, 2));
                Console.WriteLine("x/y={2} when x={0} and y={1}", 10, 2, proxy.Divide(10, 2));
            }

 終結點是WCF進行通信的唯一方式,ChannelFactory<TChannel>本質上是通過指定的終結點創建用於進行服務調用的服務代理。上述嗲嗎中,在創建ChannelFactory<TChannel>的時候在構造函數中指定了終結點的ABC三要素,其中地址和綁定則通過參數指定,契約提現在ChannelFactory<TChannel>的泛型參數上。不過一般我們也不會這么做,聰明的你猜到了,還是通過配置文件來進行,沒錯,配置如下:

<configuration>
  <system.serviceModel>
    <client>
      <endpoint
        name="CalculatorService"
        address="http://127.0.0.1:1111/CalculatorService"
        binding="wsHttpBinding"
        contract="Service.Interface.ICalculator"  />
    </client>
  </system.serviceModel>
</configuration>

在配置文件中添加了endpoint,同樣還是指定了address地址、binding綁定、以及契約constract和代碼創建的過程一致哦。

代碼修改如下:

            using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("CalculatorService"))
            {
                ICalculator proxy = channelFactory.CreateChannel();
                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}", 10, 2, proxy.Subtract(10, 2));
                Console.WriteLine("x*y={2} when x={0} and y={1}", 10, 2, proxy.Multiply(10, 2));
                Console.WriteLine("x/y={2} when x={0} and y={1}", 10, 2, proxy.Divide(10, 2));
            }

可以看到,唯一的區別就是在對ChannelFactory<TChannel>實例化的時候,使用配置文件中endpoint的name屬性替代了之前的Binding和地址。

 注:上述代碼中如果是非添加服務引用的方式進行創建Client,那么在運行Client的時候,請確保Hosting.exe已經正常啟動。

通過IIS方式寄宿服務

之前的例子是將控制台作為WCF的寄宿方式或者是直接添加契約項目的引用,然后通過配置或者是ChannelFactory的形式進行創建服務對象,其實在大多的開發中以IIS的形式創建WCF也是比較常見的。
每一個Webservice都是具有一個asmx文本文件,客戶端通過訪問.asmx文件即可實現Webservice的調用。當然WCF和Webservice類似,每一個WCF服務都有一個對應的.svc文本文件。基於IIS服務寄宿要求的WCF服務都具有相應的.svc文件,.svc文件部署於IIS站點中,對WCF服務的調用提現在對.svc文件的訪問上。.svc文件僅僅包含一個%@ ServiceHost這樣的指令,該指令具有一個必須的Service屬性和一些可選的屬性,如下代碼所示就是一個簡單的.svc文本的內容

 

<%@ ServiceHost Language="C#" Debug="true" Service="Service.CalculatorService" %>

 

其中指定了Service屬性為一個完整名稱的WCF服務類。

寄宿在IIS下的WCF服務實際上就是一個WEB應用,所以通常會把.svc文件放在Web下,並且通過web.config文件進行配置WCF的終結點和用於發布元數據的ServiceMetadataBehavior服務行為。由於服務調用是通過訪問服務對應的.svc文件來實現的,這個.svc文件所在的地址對於客戶端來說就是服務(終結點)的地址,因此是不需要配置終結點的adder的,配置代碼如下:

<configuration>
  <system.serviceModel>
    <behaviors>
  <serviceBehaviors>
        <behavior name="serviceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
 </behaviors>
    <services>
      <service name="Service.CalculatorService" behaviorConfiguration="serviceBehavior" >
        <endpoint  binding="basicHttpBinding" contract="Service.Interface.ICalculator"/>
      </service>
  </services>
  </system.serviceModel>
</configuration>

 除了終結點沒有指定address之外,服務行為ServiceMetadataBehavior用於元數據發布時也沒有指定元素的發布地址。在這種情況下,.svc文件的地址加上?wsdl查詢字符串就是元數據發布地址。對於當前例子來說,當服務被成功寄宿到本地的Web應用之后,就可以通過http://127.0.0.1:1111/網站名稱/CalculatorService.svc?wdsl得到表示服務元數據的WSDL文件,這也就是為什么我們右鍵瀏覽.svc文件的時候返回的是很長的xml內容。

再次修改客戶端:

<configuration>
  <system.serviceModel>
    <client>
      <endpoint
        name="CalculatorService"
        address="http://127.0.0.1:1111/MyWeb/CalculatorService.svc"
        binding="wsHttpBinding"
        contract="Service.Interface.ICalculator"  />
    </client>
  </system.serviceModel>
</configuration>

僅僅修改了address屬性,當前假設我們的Web應用程序叫做MyWeb,這也我們的address就可以找到發布后的.svc文件了。

 至此,我們一共介紹了兩大類的WCF的方法:
一種是將WCF服務寄宿在控制台程序:

  1.通過添加服務引用(根據元數據的地址添加),然后創建了一個Client對象,然后就可以操作了;
  2.通過添加對契約項目的引用,然后通過ChannelFactory<TChannel>創建了一個Channel對象(通過契約類型,Binding,以及一個地址);
  3.通過添加對契約項目的引用,然后通過ChannelFactory<TChannel>創建了一個Channel對象(首先會在config中配置endpoint信息,然后通過endpoint的name作為       ChannelFactory的參數進行創建)。  

一種是依靠IIS作為寄宿:

  1.添加對契約項目的引用,然后通過修改配置文件的endpoint的address為.svc的地址,這樣同樣還是通過ChannelFactory<TChannel>方式進行創建代理對象;
  2.添加契約項目的引用,然后通過ChannelFactory<TChannel>創建了一個Channel對象(通過契約類型,Binding,以及一個地址,這時候的地址應該是.svc)。

 

好了,到這里我們的整個對WCF的引用方式(以及發布WCF服務)已經介紹完畢,希望大家多提意見和建議。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM