程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF系列(一) 完全不使用配置文件構建和使用WCF服務

WCF系列(一) 完全不使用配置文件構建和使用WCF服務

編輯:關於.NET

只使用代碼而不用配置文件的情況不適合IIS為宿主的情況,IIS宿主必須使用配置文件配置WCF的ServiceHost。

1、服務端

1.1.准備Contract和實現Contract的服務

很簡單的一個Contract(Interface)和實現這個Contract(實現這個接口的類)的服務。

這是VS2005中使用add new item,選WCF Service後自動生成的一個模板例子服務代碼。

[ServiceContract()]
public interface IService
{
   [OperationContract]
   string MyOperation1(string myValue);
}
public class Service : IService
{
   public string MyOperation1(string myValue)
   {
     return "Hello: " + myValue;
   }
}

1.2.建立ServiceHost

一般使用public ServiceHost(Type serviceType, params Uri[] baseAddresses)構造方法建立ServicesHost。

參數:

Type serviceType -- 為實現了某些Contract的類的類型,為這個服務主機要host的服務。

params Uri[] baseAddresses -- 為任意數量的baseAddress。

Uri baseAddress = new Uri("http://localhost:8080/WCFService/Service");
//Instantiate new ServiceHost
myServiceHost = new ServiceHost(typeof(Service), baseAddress);

一個ServiceHost內只能駐留一個Service類,但是這個Service類可以實現多個Contract,每個Contract都能通過一個或多個(不同的bind)Endpoint向客戶端暴露。

進程、應用程序域和ServiceHost

Dotnet出現之前,資源的分配是以進程為單位,進程是應用程序的安全邊界,進程之間不能直接訪問,一個進程的崩潰也不會直接影響到別的進程。

但是,進程有個缺點,為了維護進城的安全上下文,耗費的資源很大。

後來引入了線程,一個進程中可以包含多個線程,同一進程裡的線程共享資源、切換方便,但是線程不具有隔離性,一個線程的崩潰將會影響到其他線程。

Dotnet引入了應用程序域,是介於進程和線程之間的邏輯概念,它既有進程的安全隔離性的優點,又有線程輕巧快捷的特性。應用程序域跟進程一樣,一個應用程序域不能直接訪問另一個應用程序域的資源,一個應用程序域的崩潰也不會影響其他應用程序域。同時應用程序域占用的資源比進程少的多,應用程序直接的切換也很快捷。

一個進程中可以包含多個應用程序域,一個應用程序域內有可以包含多個線程。

所有的win可執行文件(exe、dll等等)的開頭都是一個被稱作(Portable Executable)結構,dotnet的可執行文件同樣也是用了這個PE頭(結構同以前的兼容,只是增加了些內容),下面是dotnet的PE主要包含的信息:

運行這個可執行文件要求的最低CLR版本號

是否使用了強名稱

程序的入口地址

可執行文件的元數據(metadata)

簡單的dotnet的exe可執行文件的載入過程:

windows程序載入器(os loader)讀取exe文件的PE頭,獲取入口地址,exe入口地址其實是個跳轉指令指向mscoree.dll中的_corexemain函數。

_corexemain函數實際上是個入口程序,一般被稱作shim(填隙物)。由這個入口程序來決定使用哪個類型的CLR(服務器類型或工作站類型),和什麼版本的CLR來運行這個exe。

確定了使用哪個CLR後,動態載入這個CLR,把控制權交給CLR。

CLR拿到控制權後,首先創建一個工作進程,以便在進程內創建應用程序域。

CLR創建工作進程後,首先會創建一個缺省應用程序域,這個缺省應用程序域一般不用來加載用戶代碼。之後,開始在在新的應用程序域中加載用戶代碼,並運行。

具體到承載WCF的console應用,是個dotnet的exe程序,運行後被CLR加載到一個應用程序域後,ServiceHost就在這個應用程序內運行,如下圖:

在一個Application Domain中可以實例化多個 ServiceHost 實例,但每個應用程序域內只有一個 ServiceHost 實例更便於操作。您可以在一個宿主內使用多個端點公開多個服務接口。

1.3.給ServiceHost添加Endpoint

Endpoint是直接暴露給客戶端就行通訊的接口,經典的一個Endpoint可以用ABC來描述,即address – 這個Endpoint對外的訪問地址,binding – 這個Endpoint是通過什麼樣的通訊手段暴露給客戶端的,Contract -- 這個Endpoint對外暴露的是哪個Contract。

通過兩個方法給ServiceHost添加Endpoint

1.3.1.ServiceHost.AddServiceEndpoint

AddServiceEndpoint方法有8種重載,ServiceHost提供了四種:

ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address);

ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address);

ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri);

ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);

ServiceHost的父類ServiceHostBase也提供了四種:

ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address);

ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address);

ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri);

ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri);

其中參數implementedContract為Contract的完全名稱,即名稱空間.類名。

myServiceHost.AddServiceEndpoint(typeof(WCFService.IService), new BasicHttpBinding(), "");

1.3.2.ServiceHost.Description.Endpoints.Add(ServiceEndpoint item)

ServiceHost.Description 是一個 ServiceDescription 類型的對象。

ServiceDescription 是一個Service在內存中的一個完整的描述,包括服務的所有Endpoint,和每個Endpoint的各自的address、binding、contract和behaviors。

使用此方法先要根據Endpoint的ABC構造一個ServiceEndpoint 對象。

ServiceEndpoint(ContractDescription contract, Binding binding, EndpointAddress address)

其中ContractDescription這樣通過ContractDescription的靜態方法GetContract構造:

ContractDescription.GetContract(Type contractType);

ServiceEndpoint myServiceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(WCFService.IService)), new BasicHttpBinding(), new EndpointAddress(baseAddress));
myServiceHost.Description.Endpoints.Add(myServiceEndpoint);

1.4.視需要給ServiceHost添加behavior

ServiceHost.Behaviors是一個 IServiceBehavior類型的對象集合。

IserviceBehavior 提供了一個在整個服務范圍內修改或則插入定制擴展的機制。

如果需要把服務通過WSDL對外暴露對服務的Metadata描述,就需要加一個ServiceMetadataBehavior類型的Behavior:

ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = new Uri("http://localhost:8001/");
myServiceHost.Description.Behaviors.Add(behavior);  // myServiceHost是ServiceHost實例

要發布Metadata,ServiceHost必須有一個http的baseAddress,所以在構造ServiceHost實例時,就需要在構造方法中加入這個http的baseAddress,這個http的baseAddress就是對外發布的Metadata地址。

1.5.打開ServiceHost,開始提供服務

ServiceHost構建好了,添加了需要的Endpoint、behavior後,使用ServiceHost.Open()方法開發ServiceHost實例,開始對外提供服務。

2、客戶端

2.1.引用service

客戶端要訪問服務端的服務,首先要知道服務端的服務提供了什麼方法,就是要知道服務的Contract。如何取得服務端的Contract有幾種方法

2.1.1.直接把服務端的Contract的副本拷貝到客戶端

這個方法是最原始的一種方法,這樣保證了服務端跟客戶端使用同一份Contract。但是,這個方法不值得提倡,因為雙方的Contract是同一個來源,但是畢竟是兩個獨立的物理存在,它們之間只能人為的來保證其一致性。

2.1.2.使用Svcutil .exe工具獲得服務端Contract並生成本地服務代理類

大家知道,web service是通過WSDL對外提供服務的描述,以便客戶端能夠通過wsdl知道這個web service所包含的方法、方法的簽名等等信息,客戶端通過wsdl就能知道怎麼去調用這個web service。

到了WCF時代,微軟依然采用WSDL來提供對WCF服務的描述。

前面服務端給ServiceHost添加了一個ServiceMetadataBehavior類型的Behavior,目的就是讓服務端對外提供WSDL形式的服務Metadata描述。

微軟提供了Svcutil .exe工具用來通過WSDL生成客戶端Contract和代理功能:

Svcutil .exe httpbaseAddress

httpbaseAddress 就是服務端設置的http的baseAddress。當然前提是服務端在ServiceHost.Behaviors添加一個ServiceMetadataBehavior類型的Behavior,並設置HttpGetEnabled屬性為true,允許對外暴露服務端Metadata描述

運行Svcutil .exe後,生成兩個文件,一個是WCF配置文件,一個是包含了服務端Contract和對應於服務端service的本地代理類的cs文件。

生成的cs文件有下面的規律:

l 引用服務端的服務所涉及的Contract(Interface類型)基本都原樣引用到客戶端(可能會自動給Contract添加一些Attribute)。

l 服務端的Endpoint到了客戶端,每個具有不同Contract的Endpoint都會在客戶端生成一個代理類。Contract相同,binding不同的Endpoint使用同一個客戶端代理類。

2.1.3.在客戶端項目中添加Service reference

在vs2005中安裝了WCF的extention後,在項目的References上點擊右鍵,會多出來一個“Add Service Reference”的選項,這就是用來引用WCF服務的,引用地址就是服務端設置的http的baseAddress。

在這裡引用WCF服務,跟使用Svcutil .exe命令一樣,會在項目中生成同樣的兩個文件。

2.2.生成客戶端service代理實例

引用服務後,客戶端生成了配置文件和包含了Contract和本地代理類的cs文件,這裡我們完全不使用配置文件,所以把生成的配置文件從項目中排除。

2.2.1.使用ChannelFactory Generic

使用ChannelFactory Generic類的CreateChannel靜態方法CreateChannel ,返回一個客戶端代理。

static TChannel CreateChannel (Binding binding, EndpointAddress endpointAddress);

localhost.IService proxy = ChannelFactory<localhost.IService>.CreateChannel (new BasicHttpBinding(), new EndpointAddress("http://localhost:8080/WCFService/Service"));

這個方法包含了一個Endpoint的ABC三個主要元素:

Address – new EndpointAddress("http://localhost:8080/WCFService/Service")是地址。

Bingding – new BasicHttpBinding() 是綁定。

Contract -- localhost.IService是引用服務後在在客戶端生成的來自服務端的Contract(interface類型)。

2.2.2.直接使用引用服務後形成的本地代理類

上面使用ChannelFactory的CreateChannel靜態方法建立代理只使用到了引用WCF服務後在客戶端生成的Contract,同時前面也說過,引用WCF服務後,還會在同時給每個Contract不同的Endpoint生成一個繼承自System.ServiceModel .ClientBase的本地代理類。

客戶端可以直接使用多個重載的代理類構造方法實例化這些代理類。如果不使用配置文件,使用這個構造方法:

SecondServiceClient(Binding binding, EndpointAddress remoteAddress)

其中SecondServiceClient為本地的一個代理類。實例化一個代理類的代碼是這樣的:

localhost.ServiceClient proxy = new localhost.ServiceClient(new BasicHttpBinding(), new EndpointAddress("http://localhost:8080/WCFService/Service"));

同樣,實例化的proxy也包含了一個Endpoint的ABC三個主要元素:

Address – new EndpointAddress("http://localhost:8080/WCFService/Service")是地址。

Bingding – new BasicHttpBinding() 是綁定。

Contract –localhost.ServiceClient本身就是繼承自某一個Contract的

2.3.使用代理實例的方法

有了WCF的本地代理類實例,就可以使用服務提供的方法了。

string result = proxy.MyOperation1("myFirstWCF");

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved