多語系服務常見做法是將 Culture 當作參數傳遞,但 Culture 屬於基礎設施關注點,非業務邏輯一部分。理想模式:Client 自動將當前 Culture 放入 SOAP Header,Server 透過擴展點攔截、設定執行緒 Culture,服務邏輯僅專注「依據當前執行緒 Culture 取得資源」。
ICallContextInitializer 提供 BeforeInvoke (設定 Context) 與 AfterInvoke (清理 Context) 兩個切點,完美契合「臨時切換 Culture」需求。
public class CultureSettingCallContextInitializer : ICallContextInitializer
{
private const string CultureHeaderName = "__CultureInfo";
private const string CultureHeaderNs = "urn:artech.com";
public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
{
var saved = new[] { Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.CurrentUICulture };
if (message.Headers.FindHeader(CultureHeaderName, CultureHeaderNs) > -1)
{
var culture = message.Headers.GetHeader<CultureInfo>(CultureHeaderName, CultureHeaderNs);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
}
return saved; // 傳遞給 AfterInvoke
}
public void AfterInvoke(object correlationState)
{
var cultures = correlationState as CultureInfo[];
if (cultures != null)
{
Thread.CurrentThread.CurrentCulture = cultures[0];
Thread.CurrentThread.CurrentUICulture = cultures[1];
}
}
}
public class CultureSettingBehaviorAttribute : Attribute, IOperationBehavior
{
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
=> dispatchOperation.CallContextInitializers.Add(new CultureSettingCallContextInitializer());
// 其他介面成員空實作...
}
套用:[CultureSettingBehavior] 標註於 Contract 作業或 Service 實作方法。
public class CultureSettingBehavior : IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (var op in endpointDispatcher.DispatchRuntime.Operations)
op.CallContextInitializers.Add(new CultureSettingCallContextInitializer());
}
// 其他介面成員空實作...
}
public class CultureSettingBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType => typeof(CultureSettingBehavior);
protected override object CreateBehavior() => new CultureSettingBehavior();
}
設定檔註冊 behaviorExtensions,於 endpointBehaviors 參考 cultureSettingElement。
using (var scope = new OperationContextScope(proxy as IContextChannel))
{
var header = new MessageHeader<CultureInfo>(Thread.CurrentThread.CurrentUICulture);
OperationContext.Current.OutgoingMessageHeaders.Add(header.GetUntypedHeader("__CultureInfo", "urn:artech.com"));
Console.WriteLine(proxy.GetMessage());
}
實務上可結合上一篇 ClientMessageInspector 自動化此步驟。
CallContextInitializer 讓我們在不修改 Service Contract 的前提下,優雅實現基於 SOAP Header 的 Culture 流轉與執行緒切換,展現 WCF 擴展點處理基礎設施關注點的強大能力。