Jon*_*ric 37 c# async-await asp.net-web-api
下面的代码已添加到新创建的Visual Studio 2012 .NET 4.5 WebAPI项目中.
我正在尝试分配两者HttpContext.Current.User并Thread.CurrentPrincipal在异步方法中.Thread.CurrentPrincipal流量分配不正确,除非执行await Task.Yield();(或其他任何异步)(传递true到AuthenticateAsync()将导致成功).
这是为什么?
using System.Security.Principal;
using System.Threading.Tasks;
using System.Web.Http;
namespace ExampleWebApi.Controllers
{
public class ValuesController : ApiController
{
public async Task GetAsync()
{
await AuthenticateAsync(false);
if (!(User is MyPrincipal))
{
throw new System.Exception("User is incorrect type.");
}
}
private static async Task AuthenticateAsync(bool yield)
{
if (yield)
{
// Why is this required?
await Task.Yield();
}
var principal = new MyPrincipal();
System.Web.HttpContext.Current.User = principal;
System.Threading.Thread.CurrentPrincipal = principal;
}
class MyPrincipal : GenericPrincipal
{
public MyPrincipal()
: base(new GenericIdentity("<name>"), new string[] {})
{
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
笔记:
await Task.Yield();可以出现在任何地方AuthenticateAsync(),或者可以移动到GetAsync()调用后AuthenticateAsync(),它仍然会成功.ApiController.User回报Thread.CurrentPrincipal.HttpContext.Current.User总是正确地流动,即使没有await Task.Yield().Web.config包括<httpRuntime targetFramework="4.5"/>这暗示 UseTaskFriendlySynchronizationContext.Task.Delay(1000)存在而成功.Ste*_*ary 41
很有意思!它似乎Thread.CurrentPrincipal基于逻辑调用上下文,而不是每线程调用上下文.IMO这是非常不直观的,我很想知道它为什么以这种方式实现.
在.NET 4.5.中,async方法与逻辑调用上下文交互,以便它更适合于使用async方法.我有关于这个主题的博客文章 ; AFAIK是唯一记录它的地方.在.NET 4.5中,在每个async方法的开头,它为其逻辑调用上下文激活"写时复制"行为.当(如果)修改逻辑调用上下文时,它将首先创建自身的本地副本.
您可以通过System.Threading.Thread.CurrentThread.ExecutionContextBelongsToCurrentScope在监视窗口中观察来查看逻辑调用上下文的"本地性"(即,是否已复制).
如果不这样做Yield,那么在设置时Thread.CurrentPrincipal,您将创建逻辑调用上下文的副本,该副本被视为该async方法的"本地" .当async方法返回时,将丢弃该本地上下文并且原始上下文取代它(您可以看到ExecutionContextBelongsToCurrentScope返回false).
另一方面,如果你这样做Yield,那么SynchronizationContext行为就会接管.实际发生的HttpContext是捕获并用于恢复两种方法.在这种情况下,你没有看到Thread.CurrentPrincipal保存AuthenticateAsync到GetAsync; 实际发生的HttpContext是保留,然后HttpContext.UserThread.CurrentPrincipal在方法恢复之前覆盖.
如果你移动Yield到GetAsync,你会看到类似的行为:Thread.CurrentPrincipal被视为作为范围的本地修改AuthenticateAsync; 它在该方法返回时恢复其值.但是,HttpContext.User仍然设置正确,并且该值将被捕获,Yield并且当方法恢复时,它将覆盖Thread.CurrentPrincipal.
| 归档时间: |
|
| 查看次数: |
5377 次 |
| 最近记录: |