
在WCF中有兩種不同的方法可以用于創建客戶端服務對象,他們分別為:
1. 代理構造法
2. 通道工廠法
本文會從實際應用的角度上,闡述兩種方法的一些細節和優劣對比,希望通過學習本文,能掌握什么情況下使用什么樣的方式來創建客戶端服務代理對象,怎樣創建客戶端代理對象。本文重點在術,非道。
在WCF中有兩種不同的方法可以用于創建客戶端服務對象,他們分別為:
1. 代理構造法
2. 通道工廠法
本文會從實際應用的角度上,闡述兩種方法的一些細節和優劣對比,希望通過學習本文,能掌握什么情況下使用什么樣的方式來創建客戶端服務代理對象,怎樣創建客戶端代理對象。本文重點在術,非道。
構造代理法
從名字中可以看出,使用本方法創建客戶端服務代理對象,是調用了服務代理類的構造方法。這種方法非常符合創建對象的常規方法:通過new的方式產生類型實例。可在WCF中要使用此方法,卻是需要一個前提:要首先具備代理類。所以產生代理類便是此種方法首先要解決的問題了,下面就來看下WCF中創建代理類的幾種常見辦法。
在WCF中,創建代理類,可以分為下面的幾種情況:
第一種:知道服務元數據地址,通過在項目中添加服務引用。
第二種:知道服務元數據地址,通過svcutil.exe生成代理類和配置文件。
第三種:從服務契約所在的的程序集(dll,exe)中導出元數據,然后從本地元數據生成代理類。
第四種:知道元數據的地址,通過自定義的代碼生成代理類。
下面分別詳細的闡述四種方法的操作過程
第一種:知道服務元數據地址,通過在項目中添加服務引用。
這種方法非常適合初學者,生成代理類的難度基本為0,也是這幾種生成方式中最為簡單的。但前提必須事先知道元數據的發布地址。下面是操作過程:
在項目中單擊右鍵,選擇添加服務引用
出現如下的對話框
一般情況下,填寫完下面的對話框就可以點擊確定,生成代理類了,但有的時候,我們需要對代理類對特殊的設置,比如我們要生成異步操作,要更改字典集合為數組等,這時候可以點擊上圖中左下角的高級按鈕。出現如下的對話框:
經過上面的處理,就能生成代理類了,但是通過此種方法產生代理類存在一個問題,這個問題和WCF聯系不大,但還是比較重要的,比如服務端是java開發的,且采用的Soap1.1協議,那么采用上面這種方法,將無法產生匹配soap1.1的代理類,導致在調用服務的時候,出現如下的異常:
SOAP 版本可能不匹配: 出現意外的 Envelope 命名空間 http://schemas.xmlsoap.org/wsdl/。應為 http://schemas.xmlsoap.org/soap/envelope/。
這個是我在xml web service中的添加服務中發現的,新版本的添加服務引用仍熱沒有指定協議的設置。在xml web service中,解決上面問題的辦法是采用wsdl.exe,然后指定參數/protocol:SOAP
第二種:知道服務元數據地址,通過svcutil.exe生成代理類和配置文件。
在WCF中的工具中Svcutil.exe是一個非常重要的工具,使用它,我們可以導出,導入,下載元數據,生成代理類,驗證編譯好的代理。如果我們已經知道元數據的發布地址,那么通過下面的操作,可以生成代理類
打開vs2008的命令行工具
Svcutil支持不同類型的元數據地址,如net.tcp://,http://等。下面分別進行演示:
元數據地址是net.tcp://格式:
svcutil net.tcp://127.0.0.1:6547/Service/MEX /language:cs /out:Proxy.cs
元數據地址格式是http://格式
svcutil http://127.0.0.1:6548/Service/MEX /language:cs /out:Proxy.cs
生成代理類的過程和上圖類似,不再添加注釋。
第三種:從服務契約所在的的程序集(dll,exe)中導出元數據,然后從本地元數據生成代理類。
這種方式是用來處理不發布元數據,直接從服務契約所在程序集中提取元數據并生成代理類的,操作要分為兩步:
第一步:從程序集中生成元數據
svcutil Wcf.Contracts.dll
運行此命令之后,文件夾中將產生如下的幾個文件:
第二步:從元數據中生成代理類代碼
這一步將使用上一步中生成的wsdl和xsd等元數據文件,最終生成代理類:
svcutil *.wsdl *.xsd
通過上圖,可以看到已經成功生成代理類schemas.microsoft.com.2003.10.Serialization.cs和客戶端配置output.config
第四種:知道元數據的地址,通過自定義的代碼生成代理類。
除了上面的生成代理類的方法,如果我們知道了元數據的地址,還可以通過自己的代碼實現代理類的生成,自定義的方法為:

根據元數據發布地址生成代理類
/// <summary>
/// 根據元數據發布地址生成代理類
/// </summary>
/// <param name="address">元數據地址</param>
/// <param name="outPutFile">代理類文件路徑</param>
static void GenerateCode(EndpointAddress address, string outPutFile)
{
MetadataExchangeClient mexClient = new MetadataExchangeClient(address);
MetadataSet metadataSet = mexClient.GetMetadata();
WsdlImporter importer = new WsdlImporter(metadataSet);
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
ServiceContractGenerator generator = new ServiceContractGenerator(codeCompileUnit);
foreach (ContractDescription contract in importer.ImportAllContracts())
{
generator.GenerateServiceContractType(contract);
}
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
using (StreamWriter sw = new StreamWriter(outPutFile))
{
using (IndentedTextWriter textWriter = new IndentedTextWriter(sw))
{
CodeGeneratorOptions options = new CodeGeneratorOptions();
provider.GenerateCodeFromCompileUnit(codeCompileUnit, textWriter, options);
}
}
}
通過上面的方法,都可以得到代理類,在沒有雙工的情況下,服務的代理類是System.ServiceModel.ClientBase<IService>的派生類,代理類包含下面四種構造方式

非雙工代理類構造重載
public ServiceClient()



{

}


public ServiceClient(string endpointConfigurationName) :

base(endpointConfigurationName)



{

}


public ServiceClient(string endpointConfigurationName, string remoteAddress) :

base(endpointConfigurationName, remoteAddress)



{

}


public ServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :

base(endpointConfigurationName, remoteAddress)



{

}


public ServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :

base(binding, remoteAddress)



{

}


而如果服務中有雙工Duplex,那么服務的代理類將不再繼承System.ServiceModel.ClientBase<IService>,而是更改為:System.ServiceModel.DuplexClientBase<IDuplexService>,而且他的構造也會變成如下的四個:

雙工代理類構造重載
public DuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance) :

base(callbackInstance)



{

}


public DuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName) :

base(callbackInstance, endpointConfigurationName)



{

}


public DuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress) :

base(callbackInstance, endpointConfigurationName, remoteAddress)



{

}


public DuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :

base(callbackInstance, endpointConfigurationName, remoteAddress)



{

}


public DuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :

base(callbackInstance, binding, remoteAddress)



{

}


通過調用上面代理類中任意的構造函數重載,都能創建客戶端服務代理對象實例。具體使用方法可見實例項目Wcf.Client中的代碼
通道工廠法
除了上面提到的代理構造法能夠在客戶端創建代理的對象實例,我們還可以通過通道工廠的方式(ChannelFactory)來完成同樣的任務。而使用通道工廠也包含如下的幾種使用情況
第一種:知道服務應用程序終結點的地址和所使用的綁定方式的情況下。
第二種:只知道服務元數據終結點的地址,不知道應用程序終結點的具體情況下。
第一種:知道服務應用程序終結點的地址和所使用的綁定方式的情況下
在這種情況下,客戶端所掌握的服務應用程序終結點資料比較詳細,使用起來也比較方便,典型的創建方法為:

采用通道工廠的方式生成客戶端服務對象實例

/**//**//**//// <summary>

/// 采用通道工廠的方式生成客戶端服務對象實例

/// </summary>

/// <param name="bind"></param>

/// <param name="address"></param>

static void CreateClientInstanceByChannel(Binding bind, EndpointAddress address)



{

ChannelFactory<Proxys.IService> channelFactory = new ChannelFactory<Proxys.IService>(bind);

var channel = channelFactory.CreateChannel(address);

using (channel as IDisposable)



{

channel.DoWork();

Wcf.Proxys.MyData myData = channel.GetData(10);

}

}


這樣的一種方法,給我們在調試程序的情況下,提供了更大的便利。
第二種:只知道服務元數據終結點的地址,不知道應用程序終結點的具體情況下。
在這種情況下,客戶端只需要知道元數據終結點的地址,便能通過MetadataResover來獲取元數據終結點對應的應用程序終結點,通過遍歷應用程序終結點,還能對綁定方式進行刷選。下面的代碼演示了這樣一種用法。

通過MetadataResover來獲取服務終結點,創建代理實例

/**//**//**//// <summary>

/// 已知元數據終結點,不知服務終節點,通過MetadataResover來獲取服務終結點,并用通道工廠創建客戶端服務實例

/// 這種方式有個好處是不依賴服務終結點地址,數量,bind類型的情況下在客戶端對服務終結點進行刷選并使用。

/// 當然它也有缺點,那就是會比較浪費性能,最好能配合Cache來使用。

/// </summary>

/// <param name="address"></param>

static void CreateClientInstanceByMetaResover(EndpointAddress address)



{

ServiceEndpointCollection endPoints = MetadataResolver.Resolve(typeof(Proxys.IService), address);

foreach (ServiceEndpoint p in endPoints)



{

if (EndPointIsDual(p))



{

ChannelFactory<Proxys.IService> channelFactory = new ChannelFactory<Wcf.Proxys.IService>(p.Binding);

Proxys.IService ws = channelFactory.CreateChannel(p.Address);

ws.DoWork();

Wcf.Proxys.MyData myData = ws.GetData(10);

}

}

}


static bool EndPointIsDual(ServiceEndpoint endPoint)



{

if (endPoint.Binding.GetType().ToString().IndexOf("Dual") > -1)



{

return true;

}

return false;

}


到此,我們基本上已經對客戶端類生成和客戶端代理對象實例的創建有了一些認識,而且通過上面的描述,也說明了在什么情況下,應該采用那種方法,如何生成代理類,如何創建代理對象實例。下面就代理構造法和通道工廠法的優劣進行一下對比:
1) 代理構造法不依賴于服務契約,服務端和客戶端耦合度比較低,但創建方法比較復雜,使用方法比較單一,對于一次產生多個代理對象等應用情形比較難以應對,綜合看來,更適合生產環境中使用。
2) 通道工廠法使用比較簡單,也比較靈活,但依賴于服務契約,不適合生產,更適合開發調試。
本文中,講述了WCF客戶端代理對象創建技術的一些基本常識和使用技巧。下篇文章將做一步深入:如何生成元數據需要證書驗證的客戶端代理
最后,項目實例代碼: https://files.cnblogs.com/jillzhang/CreateClientInstanceWays.rar