[翻译]谈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接口的分析,我会选取论坛中碰到一些问题以展示它们在实际项目中的应用。

[本篇代码下载]

你可能感兴趣的:(WCF)