天天看點

在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的終結點:

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

繼續閱讀