典型的 WCF 調用方式有二:一是透過 SvcUtil.exe 或加入 Web 參考產生靜態代理;二是直接使用 ChannelFactory<TChannel> 建立代理。本文示範一種獨特的動態調用方式:利用 WCF 客戶端元資料架構,先以 MetadataExchangeClient 取得服務元資料,再以 WsdlImporter 將其轉換為 ServiceEndpoint,最後據此建立 ChannelFactory 進行調用。此法有助於深入理解 WCF 元資料框架體系。
沿用熟悉的計算服務:
// 服務契約
[ServiceContract(Namespace = "https://www.artech.com/")]
public interface ICalculator
{
[OperationContract]
double Add(double x, double y);
}
// 服務實作
public class CalculatorService : ICalculator
{
public double Add(double x, double y) { return x + y; }
}
// 組態:發布 MEX 端點
<endpoint address="https://127.0.0.1:3721/calculatorservice" binding="ws2007HttpBinding" contract="Artech.ServiceInvocationViaMetadata.Contracts.ICalculator" />
<endpoint address="https://127.0.0.1:3721/calculatorservice/mex" binding="mexHttpBinding" contract="IMetadataExchange" />
MetadataExchangeClient,指定 MEX 繫結。GetMetadata 取得 MetadataSet。WsdlImporter,將已知契約(ICalculator)加入其 KnownContracts。ImportAllEndpoints 取得 ServiceEndpointCollection。ChannelFactory<ICalculator>,創建代理並調用。using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Xml;
using Artech.ServiceInvocationViaMetadata.Contracts;
namespace Artech.ServiceInvocationViaMetadata.Client
{
class Program
{
static void Main(string[] args)
{
MetadataExchangeClient metadataExchangeClient = new MetadataExchangeClient(MetadataExchangeBindings.CreateMexHttpBinding());
MetadataSet metadata = metadataExchangeClient.GetMetadata(new EndpointAddress("https://127.0.0.1:3721/calculatorservice/mex"));
WsdlImporter wsdlImporter = new WsdlImporter(metadata);
// 加入已知契約
ContractDescription contract = ContractDescription.GetContract(typeof(ICalculator));
wsdlImporter.KnownContracts.Add(new XmlQualifiedName(contract.Name, contract.Namespace), contract);
ServiceEndpointCollection endpoints = wsdlImporter.ImportAllEndpoints();
using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(endpoints[0]))
{
ICalculator calculator = channelFactory.CreateChannel();
using (calculator as IDisposable)
{
try
{
Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, calculator.Add(1, 2));
}
catch (TimeoutException)
{
(calculator as ICommunicationObject).Abort();
throw;
}
catch (CommunicationException)
{
(calculator as ICommunicationObject).Abort();
throw;
}
}
}
Console.Read();
}
}
}
此方式展示了 WCF 元資料架構的靈活性:無需預先產生靜態代理,即可在執行期動態發現並調用服務。