专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »博文摘选 » WCF技术剖析的 2十 7: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序) »正文

WCF技术剖析的 2十 7: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序)

来源: 发布时间:星期六, 2009年12月26日 浏览:0次 评论:0
通过如何将个服务发布成WSDL[编程篇]介绍我们知道了如何可以通过编程或者配置方式将ServiceMetadataBehavior这样个服务形式应用到相应服务上面从而实现基于HTTP-GET或者WS-MEX元数据发布机制那么在WCF内部具体实现原理又是怎样呢?相信很多人对此都心存好奇本篇文章内容将围绕着这个主题展开

、 从WCF分发体系谈起 如果读者想对WCF内部元数据发布机制实现原理有个全面而深入了解必须对WCF服务端分发体系有个清晰认识在这里我们先对该分发体系作个概括性介绍WCF整个分发体系在进行服务寄宿(Hosting)时被构建该体系基本结构基本上可以通过图1体现

image 图1 WCF服务端分发体系

当我们创建ServiceHost对象成功寄宿某个服务后WCF会根据监听地址区别为该ServiceHost对象创建到多个ChannelDispatcher对象每个ChannelDispatcher都拥有各自ChannelListener这些ChannelListener绑定到相应监听地址监听来自外界请求对于每个ChannelListener对象有个自己具有到多个EndpoDispatcher对象和的匹配个EndpoDispatcher对应着某个终结点

而针对每个EndpoDispatcher在其时候会为的创建个运行时即DispatchRuntimeDispatchRuntime拥有系列处理请求、激活对象和执行思路方法等操作运行时对象在这里我们主要关注个称为InstanceContextProvider对象InstanceContextProvider用于提供封装有相应服务例子InstanceContext对象

2、基于WS-MEX模式下元数据发布是如何实现? 现在我们再把话题移到元数据发布上来先来谈谈基于WS-MEX协议元数据发布方式在这种元数据发布模式下服务端通过MEX终结点发布元数据客户端创建相应MEX终结点获取元数据这和般意义上服务并没有本质区别你完全可以将元数据获取当成是个某个服务而该服务就是提供元数据

如果我们通过编程或者配置方式为某个服务添加了个MEX终结点后当服务被成功寄宿后WCF会为的创建个ChannelDispatcher该ChannelDispatcher拥有个用于监听元数据请求ChannelListener监听地址及元数据发布地址基于该MEX终结点EndpoDispatcher对象也会被创建并和该ChannelDispatcher关联在在EndpoDispatcher时候关联DispatchRuntime也随的被创建和普通终结点关联DispatchRuntime基于MEX终结点DispatchRuntime同样拥有相同运行时对象集合但是由于并没有个真正用于提供元数据服务被寄宿DispatchRuntimeInstanceContextProvider(默认是PerSessionInstanceContextProvider)是获取不到包含有真正服务例子InstanceContext对象

那么如果能够定制DispatchRuntimeInstanceContextProvider使它能够正常提供个InstanceContext而该InstanceContext包含真正能够提供元数据服务例子并且服务类类实现MEX终结点契约接口IMetadataExchange那么切问题都迎刃而解实际上ServiceMetadataBehavior内部就是这么做而这个用于提供元数据服务类型是定义在WCF内部ernal类型:WSMexImpl

1: ernal WSMexImpl : IMetadataExchange

2: {

3: //其他成员

4: public IAsyncResult BeginGet(Message request, AsyncCallback callback, object state);

5: public Message EndGet(IAsyncResult result);

6: private MetadataSet GatherMetadata( dialect, identier);

7: public Message Get(Message request);

8: }



当ServiceMetadataBehaviorApplyDispatchBehavior思路方法被执行时候ServiceMetadataBehavior会创建WSMexImpl对象据此创建InstanceContext对象并将其作为MEX终结点DispatchRuntimeSingletonInstanceContext然后创建个SingletonInstanceContextProvider作为该DispatchRuntimeInstanceContextProvider那么MEX终结点DispatchRuntime就能使用其InstanceContextProvider提供封装有WSMexImpl例子InstanceContext了

上诉这些内容虽然不算负责但是要求读者对WCF例子上下文机制有清晰认识对此不太熟悉读者可以参数WCF技术剖析(卷1)第9章为了加深读者对基于WS-MEX元数据发布机制理解接下来我会作个简单例子演示

3、 例子演示:模拟ServiceMetadataBehavior实现基于WS-MEX元数据发布 接下来我会完全基于ServiceMetadataBehavior实现原理即在上面介绍原理创建个自定义服务行为用于基于WS-MEX元数据发布Source Code从这里下载首先我们先来编写些辅助性质代码由于在本例中我需要创建些和DispatchRuntime相关运行时对象而且很多对象并没有被公开出来(很多是ernal类型比如SingletonInstanceContextProvider)我需要通过反射机制来创建它们此外我们需要为某些对象些私有或者内部属性赋值同样需要利用反射所以我写了下面两个辅助思路方法:

1: using ;

2: using .Globalization;

3: using .Reflection;

4: ServiceMetadataBehaviorSimulator

5: {

6: public Utility

7: {

8: public T CreateInstance<T>( typeQname, TypeparameterTypes, object parameters) where T:

9: {

10: Type type = Type.GetType(typeQname);

11: BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;

12: ConstructorInfo constructorInfo = type.GetConstructor(bindingFlags, Type.DefaultBinder, parameterTypes, null);

13: Activator.CreateInstance(type, bindingFlags, Type.DefaultBinder, parameters, CultureInfo.InvariantCulture) as T;

14: }

15: 

16: public void SetPropertyValue(object target, propertyName, object propertyValue)

17: {

18: BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance;

19: PropertyInfo propertyInfo = target.GetType.GetProperty(propertyName, bindingFlags);

20: propertyInfo.SetValue(target, propertyValue, null);

21: }

22: }

23: }



接下来我仿照IMetadataExchange接口定义了如下个接口:IMetadataProvisionService为了简化我省略了异步模式定义Get操作(BeginGet/EndGet)Get操作Action和ReplyAction同样基于WS-Transfer规范标准定义通过ServiceContractAttribute特性将契约Name和ConfigurationName设定成IMetadataProvisionService

1: using .ServiceModel;

2: using .ServiceModel.Channels;

3: ServiceMetadataBehaviorSimulator

4: {

5: [ServiceContract(ConfigurationName = "IMetadataProvisionService", Name = "IMetadataProvisionService", Namespace = "http://schemas.microsoft.com/2006/04/mex")]

6: public erface IMetadataProvisionService

7: {

8: [OperationContract(Action = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get", ReplyAction = "http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse")]

9: Message Get(Message request);

10: }

11: }



由于Get操作返回是封装有元数据(以MetadataSet形式)消息对象为此我定义了如下个以消息契约(Message Contract)形式定义类型:MetadataMessageMetadataMessage通过MessageBodyMemberAttribute特性直接将类型为MetadataSet属性定义成消息主体成员并按照WS-MEX规范标准设置该成员名称和命名空间

1: using .ServiceModel;

2: using .ServiceModel.Description;

3: ServiceMetadataBehaviorSimulator

4: {

5: [MessageContract(IsWrapped = false)]

6: public MetadataMessage

7: {

8: public MetadataMessage(MetadataSet metadata)

9: {

10: this.Metadata = metadata;

11: }

12: 

13: [MessageBodyMember(Name = "Metadata", Namespace = "http://schemas.xmlsoap.org/ws/2004/09/mex")]

14: public MetadataSet Metadata { get; ; }

15: }

16: }



接下来我们来创建真正用于提供元数据服务类:MetadataProvisionServiceMetadataProvisionService实现了上面定义服务契约接口IMetadataProvisionService具有个MetadataSet类型属性成员Metadata在Get思路方法中通过Metadata属性表述MetadataSet创建MetadataMessage对象并将其转化成Message对象返回最终返回消息具有WS-Transfer规定Action:http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse

1: using ;

2: using .ServiceModel.Channels;

3: using .ServiceModel.Description;

4: ServiceMetadataBehaviorSimulator

5: {

6: public MetadataProvisionService : IMetadataProvisionService,

7: {

8: public MetadataSet Metadata

9: { get; private ; }

10: 

11: public MetadataProvisionService(MetadataSet metadata)

12: {

13: (null metadata)

14: {

15: throw ArgumentNullException("metadata");

16: }

17: this.Metadata = metadata;

18: }

19: 

20: public Message Get(.ServiceModel.Channels.Message request)

21: {

22: MetadataMessage message = MetadataMessage(this.Metadata);

23: TypedMessageConverter converter = TypedMessageConverter.Create(typeof(MetadataMessage), "http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse");

24: converter.ToMessage(message, request.Version);

25: }

26: }

27: }



最后我们就可以创建用于实现元数据发布服务行为了在这里使用了和ServiceMetadataBehavior相同名字并将其定义成特性那么我们就可以直接通过特性方式应用到服务类型上所有实现体现在ApplyDispatchBehavior思路方法中该思路方法先后执行以下两组操作:

  • 导出元数据:直接通过WsdlExporter将服务相关所有终结点导出生成MetadataSet需要注意在进行终结点收集时候需要过滤到MEX终结点;元数据导出所有操作实现在GetExportedMetadata思路方法中;
  • 定制MEX终结点DispatchRuntime:在ServiceHostEndpoDispatcher列表中筛选出基于MEX终结点EndpoDispatcher然后定制它们DispatchRuntime:创建SingletonInstanceContextProvider作为IntanceContextProvider;根据导出MetadataSet创建MetadataProvisionService对象并将其封装在InstanceContext中而该InstanceContext直接设定为DispatchRuntimeSingletonInstanceContext 1: using ;

    2: using .Collections.ObjectModel;

    3: using .Reflection;

    4: using .ServiceModel;

    5: using .ServiceModel.Channels;

    6: using .ServiceModel.Description;

    7: using .ServiceModel.Dispatcher;

    8: using .Xml;

    9: ServiceMetadataBehaviorSimulator

    10: {

    11: [AttributeUsage(AttributeTargets.Class)]

    12: public ServiceMetadataBehaviorAttribute:Attribute, IServiceBehavior

    13: {

    14: private const MexContractName = "IMetadataProvisionService";

    15: private const MexContractNamespace = "http://schemas.microsoft.com/2006/04/mex";

    16: private const SingletonInstanceContextProviderType = ".ServiceModel.Dispatcher.SingletonInstanceContextProvider,.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

    17: public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpo> endpos, BindingParameterCollection bindingParameters){}

    18: 

    19: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)

    20: {

    21: MetadataSet metadata = GetExportedMetadata(serviceDescription);

    22: CustomizeMexEndpos(serviceDescription, serviceHostBase, metadata);

    23: }

    24: 

    25: private MetadataSet GetExportedMetadata(ServiceDescription serviceDescription)

    26: {

    27: Collection<ServiceEndpo> endpos = Collection<ServiceEndpo>;

    28: foreach (var endpo in serviceDescription.Endpos)

    29: {

    30: (endpo.Contract.ContractType typeof(IMetadataProvisionService))

    31: {

    32: continue;

    33: }

    34: ServiceEndpo Endpo = ServiceEndpo(endpo.Contract, endpo.Binding, endpo.Address);

    35: Endpo.Name = endpo.Name;

    36: foreach (var behavior in endpo.Behaviors)

    37: {

    38: Endpo.Behaviors.Add(behavior);

    39: }

    40: endpos.Add(Endpo);

    41: }

    42: WsdlExporter exporter = WsdlExporter;

    43: XmlQualiedName wsdlServiceQName = XmlQualiedName(serviceDescription.Name, serviceDescription.Namespace);

    44: exporter.ExportEndpos(endpos, wsdlServiceQName);

    45: MetadataSet metadata = exporter.GetGeneratedMetadata;

    46: metadata;

    47: }

    48: 

    49: private void CustomizeMexEndpos(ServiceDescription description, ServiceHostBase host,MetadataSet metadata)

    50: {

    51: foreach(ChannelDispatcher channelDispatcher in host.ChannelDispatchers)

    52: {

    53: foreach(EndpoDispatcher endpo in channelDispatcher.Endpos)

    54: {

    55: (endpo.ContractName MexContractName && endpo.ContractNamespace MexContractNamespace)

    56: {

    57: DispatchRuntime dispatchRuntime = endpo.DispatchRuntime;

    58: dispatchRuntime.InstanceContextProvider = Utility.CreateInstance<IInstanceContextProvider>(SingletonInstanceContextProviderType, Type { typeof(DispatchRuntime) }, object { dispatchRuntime });

    59: MetadataProvisionService serviceInstance = MetadataProvisionService(metadata);

    60: dispatchRuntime.SingletonInstanceContext = InstanceContext(host, serviceInstance);

    61: }

    62: }

    63: }

    64: }

    65: 

    66: public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }

    67: }

    68: }



    以我们熟悉计算服务为例我们将ServiceMetadataBehaviorAttribute直接应用CalculatorService上下面是CalculatorService定义的所以让它实现我们定义IMetadataProvisionService接口是为了在进行服务寄宿是满足服务类型比如实现终结点契约接口约束如果直接使用WCF提供IMetadataExchange由于其内部进行了相应处理服务类型和MEX终结点契约接口无关时允许

    1: using ;

    2: using .ServiceModel.Channels;

    3: using .ServiceModel.Description;

    4: using Artech.Contracts;

    5: using ServiceMetadataBehaviorSimulator;

    6: Artech.Services

    7: {

    8: [ServiceMetadataBehavior]

    9: public CalculatorService : ICalculator, IMetadataProvisionService

    10: {

    11: public double Add(double x, double y)

    12: {

    13: x + y;

    14: }

    15: 

    16: public Message Get(Message request)

    17: {

    18: throw NotImplementedException;

    19: }

    20: }

    21: }



    那么在进行服务寄宿时候我们就可以采用WCF如下方式添加MEX终结点了可以看到这和WCF本身支持MEX终结点配置本上是区别是这里契约是我们自定义IMetadataProvisionService而不是IMetadataExchange

    1: <?xml version="1.0" encoding="utf-8" ?>

    2: <configuration>

    3: <system.serviceModel>

    4: <services>

    5: <service name="Artech.Services.CalculatorService">

    6: <endpo address="http://127.0.0.1:3721/calculatorservice" binding="ws2007HttpBinding" contract="Artech.Contracts.ICalculator" />

    7: <endpo address="http://127.0.0.1:9999/calculatorservice/mex"

    8: binding="mexHttpBinding" contract="IMetadataProvisionService" />

    9: </service>

    10: </services>

    11: </system.serviceModel>

    12: </configuration>



    在客户端就可以采用和般服务完全方式获取服务元数据了下面是客户端配置注意这里配置终结点并不是Calculatorservice终结点而是为了获取元数据MEX终结点地址是服务端MEX终结点地址契约是IMetadataProvisionService采用绑定是标准基于HTTPMEX绑定

    1: <?xml version="1.0" encoding="utf-8" ?>

    2: <configuration>

    3: <system.serviceModel>

    4: <client>

    5: <endpo address="http://127.0.0.1:9999/calculatorservice/mex"

    6: binding="mexHttpBinding" contract="IMetadataProvisionService"

    7: name="mex" />

    8: </client>

    9: </system.serviceModel>

    10: </configuration>



    下面是基于ChannelFactory<TChannel>创建服务代理客户端代码可以看到和服务并无 2致获取元数据最终被写入个XML文件并被打开

    1: using .Diagnostics;

    2: using .ServiceModel;

    3: using .ServiceModel.Channels;

    4: using .ServiceModel.Description;

    5: using .Text;

    6: using .Xml;

    7: using ServiceMetadataBehaviorSimulator;

    8: Artech.Client

    9: {

    10: Program

    11: {

    12: void Main( args)

    13: {

    14: using (ChannelFactory<IMetadataProvisionService> channelFactory = ChannelFactory<IMetadataProvisionService>("mex"))

    15: {

    16: IMetadataProvisionService proxy = channelFactory.CreateChannel;

    17: Message request = Message.CreateMessage(MessageVersion.Default, "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get");

    18: Message reply = proxy.Get(request);

    19: MetadataSet metadata = reply.GetBody<MetadataSet>;

    20: using (XmlWriter writer = XmlTextWriter("metadata.xml", Encoding.UTF8))

    21: {

    22: metadata.WriteTo(writer);

    23: }

    24: Process.Start("metadata.xml");

    25: }

    26: }

    27: }

    28: }



    上面应用如果正常执行包含所有元数据信息XML文件将会通过IE(假设使用IE作为开启XML文件默认应用)开启图2是运行后截图:

    image 图2 获取元数据在IE中显示

    篇中我们将采用同样方式来模拟基于HTTP-GET元数据发布时如何实现

    作者:Artech
    出处:http://artech.cnblogs.com
    本文版权归作者和博客园共有欢迎转载但未经作者同意必须保留此段声明且在文章页面明显位置给出原文连接否则保留追究法律责任权利
    Tag标签: WCF,Metadata,WS-MEX,HTTP-GET,ServiceMetadataBehavior

标签:
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: