Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MiniProfiler with Web.API 2; is there a global magic request context object?

I'm trying to setup MiniProfiler on my web api site, and having a hard time getting MiniProfiler.Current to work.

I followed the directions at miniprofiler.com, and have the following in global.asax:

protected void Application_Start()
{
  MiniProfilerEF6.Initialize();
  // other setup
}
protected void Application_BeginRequest() {
  // need to start one here in order to render out the UI
  MiniProfiler.Start();
}
protected void Application_EndRequest() {
  MiniProfiler.Stop();
}

This uses the default WebRequestProfilerProvider, which stores the actual profile object in HttpContext.Current.Items.

When I ask for MiniProfiler.Current, it looks to HttpContext.Current.

When I make a request for one of my web api URLs:

  • Application_BeginRequest creates the profiler, store it in HttpContext.Current
  • in a web api MessageHandler, I can see HttpContext.Current
  • in a web apu IActionFilter, HttpContext.Current is now null, and my attempt to MiniProfiler.Current.Step("controller:action") fails
  • my EF queries run from various services do not get recorded, as that miniprofiler hook relies MiniProfiler.Current, which relies on HttpContext.Current, which is null right now
  • Application_EndRequest fires, and HttpContext.Current is magically back, and so it wraps up the profiler and tells me how long it's been since the request began

I dug through the code, and I can create my own IProfileProvider, to store the profiler object somewhere more reliable than HttpContext.Current, but I don't know where that could be.

I spent a few hours trying things out, but couldn't find a workable solution. The problems:

  • the IProfileProvider is a global variable; all worker threads in either the MVC or Web API pipeline all have to use the same IProfileProvider
  • I can dig around in web api RequestContext.Properties to pull out the HttpContext for that request, but that doesn't really help because my IProfileProvider is global across the entire app; If I tell it to store the profile in HttpContext A, then any simultaneous requests for other HttpContexts are going to pollute the profile
  • I can't rely on any kind of threadstorage because of async/await re-using threads dynamically
  • I can't stick the profiler object in an Ninject binding with InRequestScope because InRequestScope doesn't seem to work with web api 2.1, but even if I could
  • everyone says HttpRequestMessage.Properties is the new HttpContext.Current.Items, but again, IProfileProvider is a global variable and I don't know of a way to ensure each request is looking at their version HttpRequestMessage. MiniProfiler.Current can be called from anywhere, so I guess a global IProfileProvider would have to somehow inspect the call stack to and find an HttpRequestMessage there? That sounds like madness.

I'm at a loss. What I really want is a special variable.

like image 576
Ryan Davis Avatar asked Nov 01 '22 01:11

Ryan Davis


1 Answers

The process of putting the question together I figured it out. HttpContext.Current can get lost when you async/await things: Why is HttpContext.Current null after await?

I had to make the web.config change listed there, and adjusted my filters to use Miniprofiler.Current before any awaiting.

Also discussed at https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/

like image 82
Ryan Davis Avatar answered Dec 11 '22 03:12

Ryan Davis