异步操作后的HttpContext.Current.Items

时间:2013-08-27 07:52:52

标签: c# asp.net asp.net-web-api .net-4.5 async-await

考虑以下ASP.NET Web API委派处理程序:

public class MyHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var guid = Guid.NewGuid();
        HttpContext.Current.Items["foo"] = guid;

        // An Async operation
        var result = await base.SendAsync(request, cancellationToken);

        //All code from this point is not gauranteed to run on the same thread that started the handler

        var restoredGuid = (Guid)HttpContext.Current.Items["foo"];

        //Is this gauranteed to be true
        var areTheSame = guid == restoredGuid;

        return result;
    }
}

以上示例位于委托处理程序中,我尝试修复的问题同样适用于控制器,业务对象等。

我最终试图在每个HTTP请求的各种对象之间提供一些简单的内存中共享状态

据我所知,在异步操作期间,最初运行该操作的ASP.NET线程将返回到线程池,并且在异步操作完成后可以使用不同的线程来完成请求。

这是否会影响HttpContext.Current.Items集合? 请求恢复后,Items集合中的项目是否保证在那里?

  1. 我知道使用HttpContext.Current经常不赞成 由于我完全同意的原因,这些天来更广泛的社区 与...我只是帮助某人挤出一个果酱。

  2. 将此数据存储在Request.Items集合中并不适合解决此问题,因为我的同事由于某些糟糕的设计决策而需要静态。

  3. 非常感谢

2 个答案:

答案 0 :(得分:26)

  

据我所知,在异步操作期间,最初运行该操作的ASP.NET线程将返回到线程池,并且在异步操作完成后可以使用不同的线程来完成请求。

这是正确的。但是,让我们在ASP.NET上讨论async一分钟。

async需要.NET 4.5。此外,ASP.NET 4.5在服务器端引入了“怪癖模式”,您必须关闭SynchronizationContext quirk 。您可以使用httpRuntime.targetFramework 4.5 appSettings来设置aspnet:UseTaskFriendlySynchronizationContexttrue

如果您的web.config没有其中一个条目,则async的行为未定义。有关详细信息,请参阅this post。我建议使用targetFramework设置并修复出现的任何问题。

  

这会影响HttpContext.Current.Items集合吗?当请求恢复时,Items集合中的项目是否保证在那里?

AspNetSynchronizationContextawait点之间保留当前请求上下文。其中包括HttpContext.Current(包括ItemsUser等)。

另一种可能性是CallContext.Logical[Get|Set]Data,它也会流过await个点。如果您不希望HttpContext上存在代码依赖性,但是开销略有增加,那么这很有用。

几周前我在ThatConference上发表了一篇关于async on the server side的演讲。您可能会发现幻灯片很有用,特别是那些处理Context和Thread-Local State的幻灯片。

答案 1 :(得分:5)

长话短说,通常应该。除非您使用的ConfigureAwait(false)可能会产生副作用,并且不会延续上下文。

或者尝试在您的应用中添加此设置。

<appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>

更新

请注意! 最初我把假。但它必须 true 才能使上下文流动。