WCF 扩展之我见: ICallContextInitializer

本系列索引,请见《开篇

本文介绍 ICallContextInitializer 这个用于扩展 WCF 的接口。

作 用

CallContext 指的是调用的上下文,即,执行当前调用的线程上下文。线程上下文中存放了当前的线程状态,语言环境,用户的权限等。通过 CallContextInitializer 就可以修改这些值,从而改变调用的上下文。

CallContextInitializer 最常用的场景就是根据 WCF Request 头部所携带的区域信息设置当前线程的语言环境,以及在用户登录后设置用户的一些身份凭证。

执行时机

简单来说,当服务实例被创建之后,实际发生调用之前,便会调用 CallContextInitializer 对调用的上下文进行初始化。同时,它还提供了一个对象用来保存状态改变之前的值,这样在调用结束后就可以通过该值把状态恢复成之前的样子。


结合代码来说,如果读了《InstanceProvider》那篇文章,你应该会发现在 ProcessMessage41 方法中会去调用 ProcessMessage5 并执行 InvokeBegin,而在该方法开始处便会先去调用 InitializeCallContext。

该方法最终会去调用 CallContextInitializer.BeforeInvoke 对当前的调用线程进行初始化。


InvokeBegin 的 finally 块中会去调用 AfterInvoke 去对线程环境进行重置。


如何扩展

ICallContextInitializer 接口提供了如下2个方法:

    public interface ICallContextInitializer
    {
            object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message);        
            void AfterInvoke(object correlationState);
    }


BeforeInvoke 会在调用具体的 operation 之前执行,该方法的返回值用于保存修改前的状态,该值会由 WCF 运行时传给 AfterInvoke。请注意这里的 message 参数前不提供 ref 关键字,也就意味着你无法通过拷贝的方式来访问 message。

AfterInvoke 的参数即是 BeforeInvoke 的返回值,利用该方法可以编写恢复上下文状态的逻辑。


注,请确保不要在 AfterInvoke 中抛出未处理的异常,否则会导致 WCF 运行时所在的进程异常退出,详情见《基于CallContextInitializer的WCF扩展导致的严重问题

举个栗子

又到了鸡动人心的举栗子环节了,本次的栗子从 message header 中读取出当前的语言环境,然后将其应用到当前调用的线程上。有些朋友可能会奇怪,这个功能 WCF 不身难道不提供吗?事实上,如果你启用了 AspNetCompatibility,则 WCF 会利用 ASP.NET 的能力去解析(毕竟这个能力在 Asp.Net 上是默认的),可参见《IIS 中的 CurrentUICulture 和 CurrentCulture》。

下面演示下如何通过 ICallContextInitializer 来达到这个效果:

public class MyContextInitilizer : ICallContextInitializer
{
    public void AfterInvoke(object correlationState)
    {
        Thread.CurrentThread.CurrentCulture = correlationState as CultureInfo;
    }

    public object BeforeInvoke(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.IClientChannel channel, System.ServiceModel.Channels.Message message)
    {
        string culture = message.Headers.GetHeader<string>("culture", "http://blog.chenxu.me");
        CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
        Debug.WriteLine(Thread.CurrentThread.CurrentCulture.Name);
        Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);
        return currentCulture;
    }
}


文章索引

[隐 藏]

本站采用知识共享署名 3.0 中国大陆许可协议进行许可。 ©2014 Charley Box | 关于本站 | 浙ICP备13014059号