[翻译]谈WCF的扩展功能——行为(behavior)

  本系列的第一部分关注WCF的行为(behavior,在这个语境中是个术语,不晓得要不要直译出来)。针对不同的情形,共有对应的4种behavior:service, endpoint, contract 和 operation behaviors。这些behavior接口能作为几乎所有其他扩展功能的入口点,在实现它们的过程中我们能方便地取得后者(其他扩展功能)的引用。

  描述和运行时(Description vs. Runtime)

  WCF behavior是服务描述的一部分,描述当服务开始运行时——意即宿主open时——会发生什么。当宿主实例被创建,终结点被添加,监听器尚未启动时,程序能够修改服务描述,来定义一旦服务开始运行后的behave。如果没有定义behave,那么宿主启动毫无意义

  当宿主open之后(IIS/WAS宿主是在第一个消息到达可用服务时),它将会初始化所有的监听器、路由器、过滤器、通道、拦截器等(listeners, dispatchers, filters, channels, hooks, etc.),这些是将消息正确发送到指定操作的必要条件。大多数扩展功能是服务运行时的一部分,由于运行时要在宿主open之后才初始,因此用户可能需要运行时已经准备就绪的通知。

  Behaviors  

  WCF behaviors声明为四个接口(命名空间是System.ServiceModel.Description): IServiceBehavior, IEndpointBehavior, IContractBehaviorIOperationBehavior. 它们的结构相同:

public interface I[Service/Endpoint/Contract/Operation]Behavior {
                void Validate(DescriptionObject);
                void AddBindingParameters(DescriptionObject, BindingParameterCollection);
                void ApplyDispatchBehavior(DescriptionObject, RuntimeObject);
                void ApplyClientBehavior(DescriptionObject, RuntimeObject); // not on IServiceBehavior
}

  接口中的四个方法被调用的顺序如下: 

  • Validate: 如果验证逻辑发现有某些问题(原文描述 if the validation logic finds something in the service / endpoint / contract / operation description which it deems invalid.),它将阻止宿主或客户端打开。
  • AddBindingParameters: 向BindingParameterCollection添加元素, 该集合在binding元素创建listeners / factories时使用(at runtime)。在behaviors和bindings之间添加相关对象的做法很有用。对服务behaviors来说,该方法对每个终结点都调用一次,BindingParameterCollection在为每个终结点创建监听器的时候使用。
  • Apply[Client/Dispatch]Behavior: 在这两个方法中我们能够获取并修改运行时对象。这两个方法最常被使用,其余两个往往为空实现。在之后的文章中我会针对每一个behavior的这两个方法提供实际项目中的示例代码。

  对于这四个接口中的相同方法,调用顺序为service-contract-endpoint-operation。

  给WCF添加behaviors

  behaviors能以三种方式添加:

  • Code: service / endpoint / contract / operation对象的description有一个behavior的集合的引用,通过它你可以方便地添加任意一个behavior。
  • Configuration: 对service 和endpoint behaviors来说,可以通过system.serviceModel/behaviors 节点添加,然后通过service behaviorsendpoint behaviors 指定使用哪个behavior.
  • Attributes: 除了endpoint behaviors,我们可以使用attributes来给其余三个对象添加behaviors。你可以创建一个attribute并实现上述四个behavior接口中的一个,然后将之应用到指定对象上,那么该对象的description也就有了相应的behavior。

  下面的代码展示了一个简单的服务,利用attribute和code(endpoint)来添加behavior。这段代码展示了behaviors和其中方法的调用顺序,另结果显示ApplyClientBehavior并未被调用,暂且搁下。

 

1.using System;
2.using System.Collections.ObjectModel;
3.using System.Reflection;
4.using System.ServiceModel;
5.using System.ServiceModel.Channels;
6.using System.ServiceModel.Description;
7.using System.ServiceModel.Dispatcher;
8
9.namespace Behaviors
10.{
11.    class MyServiceBehaviorAttribute : Attribute, IServiceBehavior
12.    {
13.        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
14.        {
15.            Console.WriteLine("Inside {0}.{1}"this.GetType().Name, MethodBase.GetCurrentMethod().Name);
16.        }
17
18.        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
19.        {
20.            Console.WriteLine("Inside {0}.{1}"this.GetType().Name, MethodBase.GetCurrentMethod().Name);
21.        }
22
23.        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
24.        {
25.            Console.WriteLine("Inside {0}.{1}"this.GetType().Name, MethodBase.GetCurrentMethod().Name);
26.        }
27.    }
28
29.    class MyEndpointBehavior : IEndpointBehavior
30.    {
31.        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
32.        {
33.            Console.WriteLine("Inside {0}.{1}, endpoint {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, endpoint.Name);
34.        }
35
36.        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
37.        {
38.            Console.WriteLine("Inside {0}.{1}, endpoint {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, endpoint.Name);
39.        }
40
41.        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
42.        {
43.            Console.WriteLine("Inside {0}.{1}, endpoint {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, endpoint.Name);
44.        }
45
46.        public void Validate(ServiceEndpoint endpoint)
47.        {
48.            Console.WriteLine("Inside {0}.{1}, endpoint {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, endpoint.Name);
49.        }
50.    }
51
52.    class MyContractBehaviorAttribute : Attribute, IContractBehavior
53.    {
54.        public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
55.        {
56.            Console.WriteLine("Inside {0}.{1}, contract {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, contractDescription.ContractType.Name);
57.        }
58
59.        public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
60.        {
61.            Console.WriteLine("Inside {0}.{1}, contract {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, contractDescription.ContractType.Name);
62.        }
63
64.        public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
65.        {
66.            Console.WriteLine("Inside {0}.{1}, contract {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, contractDescription.ContractType.Name);
67.        }
68
69.        public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
70.        {
71.            Console.WriteLine("Inside {0}.{1}, contract {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, contractDescription.ContractType.Name);
72.        }
73.    }
74
75.    class MyOperationBehaviorAttribute : Attribute, IOperationBehavior
76.    {
77.        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
78.        {
79.            Console.WriteLine("Inside {0}.{1}, operation {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, operationDescription.Name);
80.        }
81
82.        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
83.        {
84.            Console.WriteLine("Inside {0}.{1}, operation {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, operationDescription.Name);
85.        }
86
87.        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
88.        {
89.            Console.WriteLine("Inside {0}.{1}, operation {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, operationDescription.Name);
90.        }
91
92.        public void Validate(OperationDescription operationDescription)
93.        {
94.            Console.WriteLine("Inside {0}.{1}, operation {2}"this.GetType().Name, MethodBase.GetCurrentMethod().Name, operationDescription.Name);
95.        }
96.    }
97
98.    [ServiceContract]
99.    [MyContractBehavior]
100.    public interface ITest
101.    {
102.        [OperationContract]
103.        [MyOperationBehavior]
104.        int Add(int x, int y);
105.        [OperationContract]
106.        int Multiply(int x, int y);
107.    }
108
109.    [ServiceContract]
110.    [MyContractBehavior]
111.    public interface ITest2
112.    {
113.        [OperationContract]
114.        [MyOperationBehavior]
115.        string Echo(string text);
116.    }
117
118.    [MyServiceBehavior]
119.    public class Service : ITest, ITest2
120.    {
121.        public int Add(int x, int y) { return x + y; }
122.        public int Multiply(int x, int y) { return x * y; }
123.        public string Echo(string text) { return text; }
124.    }
125
126.    class Program
127.    {
128.        static void Main(string[] args)
129.        {
130.            string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
131.            ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
132.            ServiceEndpoint ep1 = host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "basic");
133.            ep1.Name = "Endpoint1-BasicHttp";
134.            ep1.Behaviors.Add(new MyEndpointBehavior());
135.            ServiceEndpoint ep2 = host.AddServiceEndpoint(typeof(ITest2), new WSHttpBinding(), "ws");
136.            ep2.Name = "Endpoint2-WSHttp";
137.            ep2.Behaviors.Add(new MyEndpointBehavior());
138.            Console.WriteLine("Opening the host...");
139.            host.Open();
140.            Console.WriteLine("Host opened");
141.        }
142.    }
143.}

  到底哪种添加behaviors的方式好?这没有标准答案,对不同的方案,我们可以采取不同的方式。比如说避免重新编译,那么configuration的方式是最好不过;在声明性编程上采用attribute;更改几率不大的采用code方式。(个人认为configuration是较普遍的方式,但如果要根据用户选择经常更换behaviors等类似情况则attribute方式更好。

  Coming up(不知何意)

  对特定behavior接口的分析,我会选取论坛中碰到一些问题以展示它们在实际项目中的应用。

[本篇代码下载]

posted @ 2011-08-03 03:46  罗基  阅读(5532)  评论(0编辑  收藏  举报