WCF是一个具有极高扩展度的分布式通信框架,无论是在信道层(Channel Layer)还是服务模型层(Service Model),我们都可以自定义相关组件通过相应的扩展注入到WCF运行环境中。在WCF众多可扩展点中,ICallContextInitializer可以帮助我们在服务操作执行前后完成一些额外的功能,这实际上就是一种AOP的实现方式。比如在《通过WCF Extension实现Localization》中,我通过ICallContextInitializer确保了服务操作具有和客户端一样的语言文化;在《通过WCF Extension实现Context信息的传递》中,我通过ICallContextInitializer实现上下文在客户端到服务端的自动传递。ICallContextInitializer的定义如下:
1: public interface ICallContextInitializer
2: {
3: // Methods
4: void AfterInvoke(object correlationState);
5: object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message);
6: }
昨天,李永京同学问了我一个相关的问题。问题大概是这样的,他采用ICallContextInitializer实现WCF与NHibernate的集成。具体来说是通过ICallContextInitializer实现对事务 的提交,即通过BeforeInvoke方法初始化NHibernate的Session,通过AfterInvoke提交事务。但是,这中间具有一个挺严重的问题:当执行AfterInvoke提交事务的时候,是可能抛出异常的。一旦异常从AfterInvoke抛出,整个服务端都将崩溃。我们现在就来讨论一下这个问题,以及问题产生的根源。
一、问题重现
为了重现这个问题,我写了一个很简单的例子,你可以从这里下载该例子。首先我定义了如下一个实现了ICallContextInitializer接口的自定义CallContextInitializer:MyCallContextInitializer。在AfterInvoke方法中,我直接抛出一个异常。
1: public class MyCallContextInitializer : ICallContextInitializer
2: {
3: public void AfterInvoke(object correlationState)
4: {
5: throw new Exception("调用MyCallContextInitializer.AfterInvoke()出错!");
6: }
7:
8: public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
9: {
10: return null;
11: }
12: }
然后,我们通过ServiceBehavior的方式来应用上面定义的MyCallContextInitializer。为此,我们定义了如下一个实现了IServiceBehavior接口的服务行为:MyServiceBehaviorAttribute。在ApplyDispatchBehavior方法中,将我们自定义的 MyCallContextInitializer对象添加到所有终结点的分发运行时操作的CallContextInitializer列表中。
1: public class MyServiceBehaviorAttribute : Attribute, IServiceBehavior
2: {
3: public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
4:
5: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
6: {
7: foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
8: {
9: foreach (EndpointDispatcher endpoint in dispatcher.Endpoints)
10: {
11: foreach (DispatchOperation operation in endpoint.DispatchRuntime.Operations)
12: {
13: operation.CallContextInitializers.Add(new MyCallContextInitializer());
14: }
15: }
16: }
17: }
18:
19: public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
20: }