Chapter 2.2:元数据通用查询类 MetadataHelper
2011-01-24 15:30 田志良 阅读(1183) 评论(2) 收藏 举报本章主要介绍元数据通用查询类 MetadataHelper。
有时候,客户端需要通过编程方式验证一个特定的终结点(通过地址进行识别)是否支持一个特定的契约。设想有这样一个应用程序,终端用户在安装时(甚至在运行时)指定或配置应用程序,用以使用服务并与服务交互。如果服务不支持所需的契约,应用程序就会向用户发出警告,提示配置的地址是无效的,询问是否更正地址或替换地址。为了支持这一功能,应用程序需要获取服务终结点的元数据,查看是否存在至少一个终结点支持请求的契约。为了简化对返回元数据的解析工作 ,现提供元数据通用查询类 MetadataHelper,如下所示:
using System;
using System.ServiceModel;
using System.Diagnostics;
using System.Collections.Generic;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Linq;
namespace WCF.Chapter2.Metadata.Client
{
public static class MetadataHelper
{
const int MessageSizeMultiplier = 5;
static ServiceEndpointCollection QueryMexEndpoint(string mexAddress, BindingElement bindingElement)
{
CustomBinding binding = new CustomBinding(bindingElement);
MetadataExchangeClient MEXClient = new MetadataExchangeClient(binding);
MetadataSet metadata = MEXClient.GetMetadata(new EndpointAddress(mexAddress));
MetadataImporter importer = new WsdlImporter(metadata);
return importer.ImportAllEndpoints();
}
public static ServiceEndpoint[] GetEndpoints(string mexAddress)
{
if (String.IsNullOrEmpty(mexAddress))
{
Debug.Assert(false, "Empty address");
return null;
}
Uri address = new Uri(mexAddress);
ServiceEndpointCollection endpoints = null;
if (address.Scheme == "http")
{
HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
httpBindingElement.MaxReceivedMessageSize *= MessageSizeMultiplier;
//Try the HTTP MEX Endpoint
try
{
endpoints = QueryMexEndpoint(mexAddress, httpBindingElement);
}
catch
{ }
//Try over HTTP-GET
if (endpoints == null)
{
string httpGetAddress = mexAddress;
if (mexAddress.EndsWith("?wsdl") == false)
{
httpGetAddress += "?wsdl";
}
CustomBinding binding = new CustomBinding(httpBindingElement);
MetadataExchangeClient MEXClient = new MetadataExchangeClient(binding);
MetadataSet metadata = MEXClient.GetMetadata(new Uri(httpGetAddress), MetadataExchangeClientMode.HttpGet);
MetadataImporter importer = new WsdlImporter(metadata);
endpoints = importer.ImportAllEndpoints();
}
}
if (address.Scheme == "https")
{
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.MaxReceivedMessageSize *= MessageSizeMultiplier;
//Try the HTTPS MEX Endpoint
try
{
endpoints = QueryMexEndpoint(mexAddress, httpsBindingElement);
}
catch
{ }
//Try over HTTP-GET
if (endpoints == null)
{
string httpsGetAddress = mexAddress;
if (mexAddress.EndsWith("?wsdl") == false)
{
httpsGetAddress += "?wsdl";
}
CustomBinding binding = new CustomBinding(httpsBindingElement);
MetadataExchangeClient MEXClient = new MetadataExchangeClient(binding);
MetadataSet metadata = MEXClient.GetMetadata(new Uri(httpsGetAddress), MetadataExchangeClientMode.HttpGet);
MetadataImporter importer = new WsdlImporter(metadata);
endpoints = importer.ImportAllEndpoints();
}
}
if (address.Scheme == "net.tcp")
{
TcpTransportBindingElement tcpBindingElement = new TcpTransportBindingElement();
tcpBindingElement.MaxReceivedMessageSize *= MessageSizeMultiplier;
endpoints = QueryMexEndpoint(mexAddress, tcpBindingElement);
}
if (address.Scheme == "net.pipe")
{
NamedPipeTransportBindingElement ipcBindingElement = new NamedPipeTransportBindingElement();
ipcBindingElement.MaxReceivedMessageSize *= MessageSizeMultiplier;
endpoints = QueryMexEndpoint(mexAddress, ipcBindingElement);
}
return endpoints.ToArray();
}
public static Type GetCallbackContract(string mexAddress, Type contractType)
{
if (contractType.IsInterface == false)
{
Debug.Assert(false, contractType + " is not an interface");
return null;
}
object[] attributes = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false);
if (attributes.Length == 0)
{
Debug.Assert(false, "Interface " + contractType + " does not have the ServiceContractAttribute");
return null;
}
ServiceContractAttribute attribute = attributes[0] as ServiceContractAttribute;
if (attribute.Name == null)
{
attribute.Name = contractType.ToString();
}
if (attribute.Namespace == null)
{
attribute.Namespace = "http://tempuri.org/";
}
return GetCallbackContract(mexAddress, attribute.Namespace, attribute.Name);
}
public static Type GetCallbackContract(string mexAddress, string contractNamespace, string contractName)
{
if (String.IsNullOrEmpty(contractNamespace))
{
Debug.Assert(false, "Empty namespace");
return null;
}
if (String.IsNullOrEmpty(contractName))
{
Debug.Assert(false, "Empty name");
return null;
}
try
{
ServiceEndpoint[] endpoints = GetEndpoints(mexAddress);
foreach (ServiceEndpoint endpoint in endpoints)
{
if (endpoint.Contract.Namespace == contractNamespace && endpoint.Contract.Name == contractName)
{
return endpoint.Contract.CallbackContractType;
}
}
}
catch
{ }
return null;
}
public static bool QueryContract(string mexAddress, Type contractType)
{
if (contractType.IsInterface == false)
{
Debug.Assert(false, contractType + " is not an interface");
return false;
}
object[] attributes = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false);
if (attributes.Length == 0)
{
Debug.Assert(false, "Interface " + contractType + " does not have the ServiceContractAttribute");
return false;
}
ServiceContractAttribute attribute = attributes[0] as ServiceContractAttribute;
if (attribute.Name == null)
{
attribute.Name = contractType.ToString();
}
if (attribute.Namespace == null)
{
attribute.Namespace = "http://tempuri.org/";
}
return QueryContract(mexAddress, attribute.Namespace, attribute.Name);
}
public static bool QueryContract(string mexAddress, string contractNamespace, string contractName)
{
if (String.IsNullOrEmpty(contractNamespace))
{
Debug.Assert(false, "Empty namespace");
return false;
}
if (String.IsNullOrEmpty(contractName))
{
Debug.Assert(false, "Empty name");
return false;
}
try
{
ServiceEndpoint[] endpoints = GetEndpoints(mexAddress);
return endpoints.Any(endpoint => endpoint.Contract.Namespace == contractNamespace && endpoint.Contract.Name == contractName);
}
catch
{ }
return false;
}
public static string[] GetContracts(string mexAddress)
{
return GetContracts(typeof(Binding), mexAddress);
}
public static string[] GetContracts(Type bindingType, string mexAddress)
{
Debug.Assert(bindingType.IsSubclassOf(typeof(Binding)) || bindingType == typeof(Binding));
ServiceEndpoint[] endpoints = GetEndpoints(mexAddress);
List<string> contracts = new List<string>();
string contract;
foreach (ServiceEndpoint endpoint in endpoints)
{
if (bindingType.IsInstanceOfType(endpoint.Binding))
{
contract = endpoint.Contract.Namespace + " " + endpoint.Contract.Name;
if (contracts.Contains(contract) == false)
{
contracts.Add(contract);
}
}
}
return contracts.ToArray();
}
public static string[] GetAddresses(string mexAddress, Type contractType)
{
if (contractType.IsInterface == false)
{
Debug.Assert(false, contractType + " is not an interface");
return new string[] { };
}
object[] attributes = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false);
if (attributes.Length == 0)
{
Debug.Assert(false, "Interface " + contractType + " does not have the ServiceContractAttribute");
return new string[] { };
}
ServiceContractAttribute attribute = attributes[0] as ServiceContractAttribute;
if (attribute.Name == null)
{
attribute.Name = contractType.ToString();
}
if (attribute.Namespace == null)
{
attribute.Namespace = "http://tempuri.org/";
}
return GetAddresses(mexAddress, attribute.Namespace, attribute.Name);
}
public static string[] GetAddresses(string mexAddress, string contractNamespace, string contractName)
{
ServiceEndpoint[] endpoints = GetEndpoints(mexAddress);
List<string> addresses = new List<string>();
foreach (ServiceEndpoint endpoint in endpoints)
{
if (endpoint.Contract.Namespace == contractNamespace && endpoint.Contract.Name == contractName)
{
Debug.Assert(addresses.Contains(endpoint.Address.Uri.AbsoluteUri) == false);
addresses.Add(endpoint.Address.Uri.AbsoluteUri);
}
}
return addresses.ToArray();
}
public static string[] GetAddresses(Type bindingType, string mexAddress, Type contractType)
{
Debug.Assert(bindingType.IsSubclassOf(typeof(Binding)) || bindingType == typeof(Binding));
if (contractType.IsInterface == false)
{
Debug.Assert(false, contractType + " is not an interface");
return new string[] { };
}
object[] attributes = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false);
if (attributes.Length == 0)
{
Debug.Assert(false, "Interface " + contractType + " does not have the ServiceContractAttribute");
return new string[] { };
}
ServiceContractAttribute attribute = attributes[0] as ServiceContractAttribute;
if (attribute.Name == null)
{
attribute.Name = contractType.ToString();
}
if (attribute.Namespace == null)
{
attribute.Namespace = "http://tempuri.org/";
}
return GetAddresses(bindingType, mexAddress, attribute.Namespace, attribute.Name);
}
public static string[] GetAddresses(Type bindingType, string mexAddress, string contractNamespace, string contractName)
{
Debug.Assert(bindingType.IsSubclassOf(typeof(Binding)) || bindingType == typeof(Binding));
ServiceEndpoint[] endpoints = GetEndpoints(mexAddress);
List<string> addresses = new List<string>();
foreach (ServiceEndpoint endpoint in endpoints)
{
if (bindingType.IsInstanceOfType(endpoint.Binding))
{
if (endpoint.Contract.Namespace == contractNamespace && endpoint.Contract.Name == contractName)
{
Debug.Assert(addresses.Contains(endpoint.Address.Uri.AbsoluteUri) == false);
addresses.Add(endpoint.Address.Uri.AbsoluteUri);
}
}
}
return addresses.ToArray();
}
public static string[] GetOperations(string mexAddress, Type contractType)
{
if (contractType.IsInterface == false)
{
Debug.Assert(false, contractType + " is not an interface");
return new string[] { };
}
object[] attributes = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false);
if (attributes.Length == 0)
{
Debug.Assert(false, "Interface " + contractType + " does not have the ServiceContractAttribute");
return new string[] { };
}
ServiceContractAttribute attribute = attributes[0] as ServiceContractAttribute;
if (attribute.Name == null)
{
attribute.Name = contractType.ToString();
}
if (attribute.Namespace == null)
{
attribute.Namespace = "http://tempuri.org/";
}
return GetOperations(mexAddress, attribute.Namespace, attribute.Name);
}
public static string[] GetOperations(string mexAddress, string contractNamespace, string contractName)
{
ServiceEndpoint[] endpoints = GetEndpoints(mexAddress);
List<string> operations = new List<string>();
foreach (ServiceEndpoint endpoint in endpoints)
{
if (endpoint.Contract.Namespace == contractNamespace && endpoint.Contract.Name == contractName)
{
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
Debug.Assert(operations.Contains(operation.Name) == false);
operations.Add(operation.Name);
}
break;
}
}
return operations.ToArray();
}
public static Binding GetBinding(string address)
{
if (String.IsNullOrEmpty(address))
{
Debug.Assert(false, "Empty address");
return null;
}
string baseAddress = GetBaseAddress(address) + "?wsdl";
ServiceEndpoint[] endpoints = GetEndpoints(address);
foreach (ServiceEndpoint endpoint in endpoints)
{
if (endpoint.Address.Uri.AbsoluteUri == address)
{
return endpoint.Binding;
}
}
return null;
}
static string GetBaseAddress(string address)
{
string[] segments = address.Split('/');
return segments[0] + segments[1] + segments[2] + "/";
}
}
}
GetEndpoints() 方法对元数据交换地址的样式进行了解析。根据找到的传输样式,GetEndpoints() 方法创建了一个需要使用的绑定元素, 这样就可以设置它的 MaxReceivedMessageSize 属性值。MaxReceiveMessageSize 的默认值为 64K 。它适用于简单的服务。如果服务包含多个终结点 ,终结点又使 用了复杂类型,就会生成更大的消息。此时,调用 MetadataExchangeClient.GetMetadata() 方法就会失败。根据经验,大多数情况下最合适的倍数因子是 5。接着,GetEndpoints() 调用了 QueryMexEndpoint() 私有方法,以获取元数据。
QueryMexEndpoint() 方法接收元数据交换终结点的地址以及要使用的绑定元素。使用绑定元素是为了创建定制绑定,并将它提供给 MetadataExchange-Client 实例。MetadataExchangeClient 实例能够获取元数据,返回终结点集合。
QueryContract() 方法首先会验证传入的Type类型是否是接口类型,如果是,则判断该接口是否标记了 ServiceContract 特性。因为 ServiceContract特性可以为契约的请求类型指定名称和命名空间的别名,QueryContract()使用这些值查询符合条件的契约。如果没有指定别名, QueryContract()方法则使用类型的名字与默认的命名空间 http :// tem puri.org,然后调用另一个重载版本的 QueryContract()方法,它能够操作契约的名称和命名空间。该版本的 QueryContract() 方法调用了GetEndpoints()方法,以获得终结点数组,然后遍历该数组。如果找到至少一个终结点支持该契约,则返回 true。不管出现何种错误, QueryContract()方法都会返回false。
浙公网安备 33010602011771号