[翻译]谈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, IContractBehavior 和IOperationBehavior. 它们的结构相同:
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 behaviors 或endpoint behaviors 指定使用哪个behavior.
- Attributes: 除了endpoint behaviors,我们可以使用attributes来给其余三个对象添加behaviors。你可以创建一个attribute并实现上述四个behavior接口中的一个,然后将之应用到指定对象上,那么该对象的description也就有了相应的behavior。
下面的代码展示了一个简单的服务,利用attribute和code(endpoint)来添加behavior。这段代码展示了behaviors和其中方法的调用顺序,另结果显示ApplyClientBehavior并未被调用,暂且搁下。
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接口的分析,我会选取论坛中碰到一些问题以展示它们在实际项目中的应用。
[本篇代码下载]