5
votes

I'm building an ASP.NET WebApi 2.1 app that needs an equivalent of HttpContext.Items as a per request cache.

I cannot use HttpContext even under IIS hosting because HttpContext seems to be lost when i'm doing async work (using TPL calls, not async/await due to some interface that needs to be matched) in services/repos layers (HttpContext.Current becomes null).

I'm using unity 3.5 and cannot achieve to do a proper per request injection. Tried the HttpControllerActivator method :

public class HttpControllerActivator : IHttpControllerActivator
{
    private readonly IUnityContainer _container;
    private readonly IHttpControllerActivator _activator;

    public HttpControllerActivator(IUnityContainer container, IHttpControllerActivator activator)
    {
        _container = container;
        _activator = activator;
    }

    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        IHttpController controller = _activator.Create(request, controllerDescriptor, controllerType);
        _container.RegisterInstance<System.Net.Http.HttpRequestMessage>(request, new HierarchicalLifetimeManager());

        return controller;

    }
}

But this register the HttpRequestMessage on the root container, not the child one created by the BeginScope() call inside _activator.Create. As a result, I'm getting mixed requests instances under concurrent load.

Any idea how to solves this ? I'm searching online since two days and haven't find any real solution to that matter...

1

1 Answers

2
votes

using TPL calls, not async/await due to some interface that needs to be matched

I recommend you take another look at async and await. It is possible to use async for your part of the implementation and have it interoperate with other asynchronous APIs.

That said, if you want to preserve HttpContext.Current (as well as culture, etc.), then the key is SynchronizationContext. I have an MSDN article on that type that you may find helpful. Since your code is using TPL, you would probably want to capture the request context into a task scheduler:

var requestContext = TaskScheduler.FromCurrentSynchronizationContext();

and then use that to schedule your task continuations.

The other important aspect of asynchronous work on ASP.NET is to ensure that the runtime is aware of your asynchronous work. You can do this by calling AsyncOperationManager.CreateOperation to register asynchronous work before it starts and AsyncOperation.OperationCompleted to notify the runtime that the asynchronous work has completed. Alternatively, you can capture the SynchronizationContext.Current and call SynchronizationContext.OperationStarted and SynchronizationContext.OperationCompleted yourself.

Again, take another look at async and await and see if it's at all possible to use them; they'll take care of all the gritty details like this for you.