Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConfigureAwait(false) not making HttpContext NULL as expected

When using async/await, to avoid deadlocks, it is recommended to use ConfigureAwait(false) to all the way down to the last layer. I understand that using ConfigureAwait(false) will also make the current HttpContext null. Article

Below is the sample code just to understand why ConfigureAwait(false) is not making HttpContext NULL when expected, and making it NULL when it is NOT expected.

Controller

public class MyController : Controller
{
    private readonly MyService _service = new MyService();
    private readonly MyMapper _mapper = new MyMapper();        


    public async Task<ActionResult> DoSomething()
    {
        var data = await _service.GetData().ConfigureAwait(false);

        // EXPECTED: Below, HttpContext should NOT NULL
        // ACTUAL: HttpContext is NOT NULL as expected
        if (HttpContext == null)
        {
            throw new ArgumentNullException("context");
        }

        var model = _mapper.Map(data);

        return View(model);
    }
}

A Service that makes async call to datasource to GetData

public class MyService
{
    public async Task<string> GetData()
    {
        // EXPECTED: HttpContext should be NULL here since we are calling 
        //this method from the Controller with ConfigureAwait(false)     

        // ACTUAL: HttpContext is NOT NULL, WHY?
        if(HttpContext.Current == null)
        {
            throw new ArgumentNullException("context");
        }

        using (var client = new HttpClient())
        {
            var response = await client.GetAsync("http://url").ConfigureAwait(false);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        }
    }
}

A mapper class maps the data to model

public class MyMapper
{
    public MyModel Map(string data)
    {
        // EXPECTED:  HttpContext should NOT NULL here since async call is already done.
        // ACTUAL: HttpContext is NULL here. WHY?
        if (HttpContext.Current == null)
        {
            throw new ArgumentNullException("context");
        };

        return new MyModel()
        {
            Message = data
        };
    }
}

The HttpContext is expected to be NULL inside MyService and expected to be NOT NULL inside MyMapper, but that is not the case. Please see my inline comments inside MyService and MyMapper

like image 988
LP13 Avatar asked Apr 11 '18 17:04

LP13


1 Answers

You have it backwards. HttpContext.Current will be null after the context switch, not before.

So:

// ACTUAL: HttpContext is NOT NULL, WHY?
if(HttpContext.Current == null)
{
    throw new ArgumentNullException("context");
}

No, it is expected to be not null since there hasn't been any await so far (the parent await does not apply).

// EXPECTED:  HttpContext should NOT NULL here since async call is already done.
// ACTUAL: HttpContext is NULL here. WHY?
if (HttpContext.Current == null)

The switch happened after this line

var response = await client.GetAsync("http://url").ConfigureAwait(false);

So it is expected for it to be null.

like image 96
Camilo Terevinto Avatar answered Sep 25 '22 00:09

Camilo Terevinto