5

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...

blemasle
  • 63
  • 1
  • 4

1 Answers1

2

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.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thanks for the clarification about `SynchronizationContext` Actually, those async operation exists in my app because of the use of BookSleeve. Since I added `TaskScheduler.FromCurrentSynchronizationContext()`, I don't enter in the continuation task anymore. Also, I'm not really a fan of using that old AsymcOperationManager all over the place. All that convinced me that `async` and `await` are the way to go althought I realy don't like the fact that I have to declare my interfaces with Task results ! – blemasle May 05 '14 at 08:58
  • Yes, `Task` is a bit obnoxious in interfaces, very similar to `IDisposable`: You have to guess whether any derived type *might* be asynchronous (or have resources that need disposing). – Stephen Cleary May 05 '14 at 13:22
  • Well, due to [that bug](http://social.msdn.microsoft.com/Forums/en-US/967d4e61-d4ff-4b47-951b-c92f83146c79/tpl-inheritance-generics-verificationexception), I might not be able to use async on the functions that really need to : they have type constraints. I gave another shot to the `TaskScheduler` thing but whenever I use it, my call just hangs forever (I suspect asp.net to not "see" the task being ended). Any ideas ? – blemasle May 05 '14 at 16:16
  • OK, I found why my code hangs with this [post](http://stackoverflow.com/questions/10343632/httpclient-getasync-never-returns-when-using-await-async/10351400#10351400). Now i'm officially stuck because I either loose the HttpContext or my code hangs... – blemasle May 05 '14 at 16:47
  • If you're using `async` all the way, then you won't have `HttpContext` or deadlock problems. – Stephen Cleary May 06 '14 at 00:12