天天看點

在WCF中擷取服務中繼資料資訊

所謂擷取WCF的服務中繼資料(Metadata),歸根結點,實際上就是擷取服務的終結點 (Endpoint)的資訊,這是服務公開在外的資料資訊,包括Address、Binding與Contract,也就是所謂的ABCs。Juval Löwy在《Programming WCF Services》一書中,用生動形象的棒棒糖表示了終結點的構成:

在WCF中擷取服務中繼資料資訊

WCF服務可能包含多個終結點,每個終結點相當于是通信的入口,用戶端和服務端通過終結點交換資訊,如下圖所示:

在WCF中擷取服務中繼資料資訊

因而,如果能夠擷取終結點的詳細資訊,有助于我們更好地剖析服務的定義、内容與執行方式。

服務有兩種方案可以釋出自己的中繼資料。一種是基于HTTP-GET協定提供中繼資料;另一種則為中繼資料交換方式,它往往使用一個專門的終結點,稱之為 中繼資料交換終結點。中繼資料交換終結點與其它終結點相似,仍然包含了位址、綁定與契約,但是使用的服務契約為WCF提供的接口 IMetadataExchange。

實際上,這兩種釋出中繼資料的方式代表了它使用了兩種不同的标準協定,前者為HTTP/GET請求,後者為WS-MetadataExchange(MEX)。在WCF,以MetadataExchangeClientMode枚舉類型表示這兩種中繼資料交換模式:

public enum MetadataExchangeClientMode

{

   MetadataExchange,

   HttpGet

}

WCF為終結點定義了一個專門的ServiceEndpoint類,被定義在System.ServiceModel.Description命名 空間中。ServiceEndpoint類包含了EndpointAddress,Binding,ContractDescription三個類型的屬 性,分别對應Endpoint的Address,Binding,Contract,如下圖:

在WCF中擷取服務中繼資料資訊

要擷取服務的終結點,可以通過抽象類MetadataImporter擷取,類的定義如下:

public abstract class MetadataImporter

    public abstract Collection<ContractDescription> ImportAllContracts();

    public abstract ServiceEndpointCollection ImportAllEndpoints();

    //其它方法略;

在類中,最重要的一個方法是ImportAllEndpoints(),它能夠擷取服務的所有終結點,并傳回一個 ServiceEndpointCollection類型的對象。該類型為一個終結點集合,可以通過調用 ServiceEndpointCollection的Find()方法或FindAll()方法,找到符合條件的一個或多個終結點。它的定義如下:

public class ServiceEndpointCollection : Collection<ServiceEndpoint>

    public ServiceEndpoint Find(Type contractType);

    public ServiceEndpoint Find(Uri address);

    public Collection<ServiceEndpoint> FindAll(Type contractType);

    //其它成員略

我們可以通過契約類型,或者服務契約的位址,查找符合條件的終結點。

MetadataImporter類隻是一個抽象類,如果要擷取WSDL中繼資料,還會需要使用繼承它的子類型WsdlImporter:

public class WsdlImporter : MetadataImporter

    public WsdlImporter(MetadataSet metadata);

    public Collection<Binding> ImportAllBindings();

    public override Collection<ContractDescription> ImportAllContracts();

    public override ServiceEndpointCollection ImportAllEndpoints();

    public ServiceEndpointCollection ImportEndpoints(Binding wsdlBinding);

    //其它成員略;

如果要使用WsdlImporter,需要為其構造函數傳遞一個MetadataSet類型的對象。而MetadataSet類型的對象則可以通過 MetadataExchangeClient類的GetMetadata()方法獲得。MetadataExchangeClient類的定義如下所 示:

public class MetadataExchangeClient

    public MetadataExchangeClient();

    public MetadataExchangeClient(Binding mexBinding);

    public MetadataExchangeClient(EndpointAddress address);

    public MetadataExchangeClient(string endpointConfigurationName);

    public MetadataExchangeClient(Uri address, MetadataExchangeClientMode mode);

    public MetadataSet GetMetadata();

    public MetadataSet GetMetadata(EndpointAddress address);

    public MetadataSet GetMetadata(Uri address, MetadataExchangeClientMode mode);

假定服務公開的中繼資料位址為http://localhost:8001/IMyService?wsdl,則擷取服務中繼資料的方法如下:

string mexAddress = "http://localhost:8001/IMyService?wsdl";

BasicHttpBinding binding = new BasicHttpBinding();

MetadataExchangeClient mexClient = new MetadataExchangeClient(binding);

MetadataSet metadata = mexClient.GetMetadata(new Uri(mexAddress), MetadataExchangeClientMode.HttpGet);

MetadataImporter importer = new WsdlImporter(metadata);

ServiceEndpointCollection endpoints = importer.ImportAllEndpoints();

注意,如果是HttpGet模式,則中繼資料位址的字尾必須為?wsdl。由于我們在調用MetadataExchangeClient的 GetMetadata()方法時,傳遞的MetadataExchangeClientMode枚舉參數值為HttpGet,是以擷取的為基于 HTTP-GET的中繼資料。

如果服務使用的協定為HTTP或者HTTPS,則可能使用中繼資料交換終結點,也可能為Http-Get模式。此時,我們可以先擷取中繼資料交換終結點,如果沒有找到,再擷取基于HTTP-GET的終結點:

string mexAddress = “http://localhost:8001/IMyService?wsdl”;

MetadataSet metadata = mexClient.GetMetadata(new EndpointAddress(mexAddress));

if (endpoints == null)

    string httpGetAddress = mexAddress;

    if (!mexAddress.EndsWith(“?wsdl”) )

    {

        httpGetAddress += “?wsdl”;

    }

    BasicHttpBinding binding = new BasicHttpBinding();

    MetadataExchangeClient mexClient = new MetadataExchangeClient(binding);

    MetadataSet metadata = mexClient.GetMetadata(new Uri(mexAddress), MetadataExchangeClientMode.HttpGet);

    MetadataImporter importer = new WsdlImporter(metadata);

    endpoints = importer.ImportAllEndpoints();

在獲得ServiceEndpointCollection集合對象後,就可以針對每個ServiceEndpoint擷取終結點的Address、Binding、Contract的資訊,如下所示:

foreach (ServiceEndpoint endpoint in endpoints)

    Console.WriteLine(“Endpoint Name is {0}”, endpoint.Name);

    Console.WriteLine(“Address is {0}”, endpoint.Address.Uri.AbsoluteUri);

    Console.WriteLine(“Binding is {0}”, endpoint.Binding.GetType().ToString());

    Console.WriteLine(“Address is {0}”, endpoint.Contract.Name);

    Console.WriteLine();

繼續閱讀