从一个开始WCF扩展点总结 http://sankarsan.wordpress.com/2008/12/28/wcf-extension-points-dispatcher/,您会看到专门为解决您的问题而设计的。它被称为调用上下文初始化器 http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.icallcontextinitializer%28v=vs.110%29.aspx。看看这个文章给出了 CallContextInitializer 示例代码 http://blogs.msdn.com/b/carlosfigueira/archive/2012/02/14/wcf-extensibility-initializers-instance-context-channel-call-context.aspx.
如果您进行 ICallContextInitializer 扩展,您将获得对 BeginXXX 线程上下文的控制ANDEndXXX 线程上下文。您是说 ClaimsAuthorizationManager 已在 BeginXXX(...) 方法中正确建立了用户主体。在这种情况下,您可以为自己创建一个自定义 ICallContextInitializer,它可以分配或记录 CurrentPrincipal,具体取决于它是处理 BeginXXX() 还是 EndXXX()。就像是:
public object BeforeInvoke(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.IClientChannel channel, System.ServiceModel.Channels.Message request){
object principal = null;
if (request.Properties.TryGetValue("userPrincipal", out principal))
{
//If we got here, it means we're about to call the EndXXX(...) method.
Thread.CurrentPrincipal = (IPrincipal)principal;
}
else
{
//If we got here, it means we're about to call the BeginXXX(...) method.
request.Properties["userPrincipal"] = Thread.CurrentPrincipal;
}
...
}
为了进一步澄清,考虑两种情况。假设您实现了 ICallContextInitializer 和 IParameterInspector。假设这些挂钩预计使用同步 WCF 服务和异步 WCF 服务(这是您的特殊情况)执行。
以下是事件的顺序以及所发生事件的解释:
同步案例
ICallContextInitializer.BeforeInvoke();
IParemeterInspector.BeforeCall();
//...service executes...
IParameterInspector.AfterCall();
ICallContextInitializer.AfterInvoke();
上面的代码没有什么奇怪的。但现在看看下面异步服务操作会发生什么......
异步案例
ICallContextInitializer.BeforeInvoke(); //TryGetValue() fails, so this records the UserPrincipal.
IParameterInspector.BeforeCall();
//...Your BeginXXX() routine now executes...
ICallContextInitializer.AfterInvoke();
//...Now your Task async code executes (or finishes executing)...
ICallContextInitializercut.BeforeInvoke(); //TryGetValue succeeds, so this assigns the UserPrincipal.
//...Your EndXXX() routine now executes...
IParameterInspector.AfterCall();
ICallContextInitializer.AfterInvoke();
正如您所看到的,CallContextInitializer 确保您有机会在 EndXXX() 例程运行之前初始化值,例如 CurrentPrincipal。因此,EndXXX() 例程确实在与 BeginXXX() 例程不同的线程上执行并不重要。是的,System.ServiceModel.Channels.Message
即使线程发生更改,WCF 也会保留并正确传输在 Begin/End 方法之间存储用户主体的对象。
总的来说,这种方法允许您的 EndXXX(IAsyncresult) 使用正确的 IPrincipal 执行,而无需在 EndXXX() 例程中显式地重新建立 CurrentPrincipal。与任何 WCF 行为一样,您可以决定这是否适用于单个操作、合约上的所有操作或端点上的所有操作。