从
WCF扩展点概述开始,您将看到一个专门设计来解决您问题的扩展点。它被称为
CallContextInitializer。请查看这篇
文章,其中提供了CallContextInitializer示例代码。
如果您创建一个ICallContextInitializer扩展,您将控制BeginXXX线程上下文和EndXXX线程上下文。您说ClaimsAuthorizationManager已经在您的BeginXXX(...)方法中正确地建立了用户主体。在这种情况下,您可以创建一个自定义的ICallContextInitializer,根据处理BeginXXX()还是EndXXX(),分配或记录CurrentPrincipal。例如:
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();
IParameterInspector.BeforeCall();
ICallContextInitializer.AfterInvoke();
ICallContextInitializercut.BeforeInvoke();
IParameterInspector.AfterCall();
ICallContextInitializer.AfterInvoke();
正如您所看到的,CallContextInitializer确保您有机会在EndXXX()例程运行之前初始化值,例如您的CurrentPrincipal。因此,即使EndXXX()例程肯定在与BeginXXX()例程不同的线程上执行,也没有关系。是的,存储您的用户主体在Begin/End方法之间的System.ServiceModel.Channels.Message对象会被WCF保留并正确传输,即使线程发生了变化。
总的来说,这种方法允许EndXXX(IAsyncresult)使用正确的IPrincipal执行,而无需在EndXXX()例程中显式重新建立CurrentPrincipal。与任何WCF行为一样,您可以决定是否适用于单个操作、合同上的所有操作或端点上的所有操作。
client.Method1
,调用将被路由到MethodAsync
。 - Panagiotis Kanavos