Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AsyncLocal Value is Null after being set from within Application_BeginRequest()

In the following example, I am setting a value to an AsyncLocal<string> variable on my HttpApplication subclass (i.e. Global.asax) from within Application_BeginRequest():

public class Global : System.Web.HttpApplication
{
    public static AsyncLocal<string> AsyncLocalState = new AsyncLocal<string>();

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        AsyncLocalState.Value = HttpContext.Current.Request.Path;
    }

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
        var path = AsyncLocalState.Value;
    }

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        var path = AsyncLocalState.Value;
    }
}

Later on, I will attempt to access the value of this AsyncLocal variable from within a handler, such as an MVC action method, or even just a plain IHttpHandler.

If I send a large enough request (e.g. a POST with more than 15KB of data -- the larger the request, the easier it is to observe), there is a very good chance that the value of AsyncLocalState is NULL when accessed from a handler even though it was set on BeginRequest.

This is reproducible from a brand-new ASP.NET project without any other libraries/modules/handlers loaded.

Is this a bug? Or maybe I'm doing something wrong? Or is ASP.NET just too unstable for this?

Addition note: the exact same behavior is observed if I instead use CallContext.LogicalGetData/CallContext.LogicalSetData.

Platform: ASP.NET, .NET 4.6.2, on Windows 7

Update: After trying to dig around, I've found a lot of references to, but nothing authoritatively saying that the ExecutionContext does not flow between ASP.NET pipeline events (except when it does?). And both AsyncLocal and the logical call context are based on the ExecutionContext.

like image 301
Joseph Daigle Avatar asked Apr 13 '17 11:04

Joseph Daigle


1 Answers

The closest thing to an authoritative answer is this comment by David Fowl on GitHub.

The ExecutionContext does not flow between ASP.NET pipeline events if these events do not execute synchronously. Therefore, don't use AsyncLocal or the logical CallContext to persist state; use HttpContext.Items.

Update: .NET 4.7.1 adds a new callback method, HttpApplication.OnExecuteRequestStep, which per documentation "provides extensibility to the ASP.NET pipeline to make it easy for developers to implement features in ambient context scenarios and build libraries that care about ASP.NET execution flow (for example, tracing, profiling, diagnostics, and transactions)."

This is precisely what someone would need in order to restore the AsyncLocal state or the logical CallContext between ASP.NET pipeline events.

like image 138
Joseph Daigle Avatar answered Nov 15 '22 18:11

Joseph Daigle