Passing HttpContext to HttpResponse.OnStarting - c#

Project that I'm working on contains middleware that uses OnStarting method and it passes HttpContext as a second argument like this:
context.Response.OnStarting(async state =>
{
var context = (HttpContext)state;
await SetSafeStatusCode(context);
}, context);
await _next(context);
I've seen examples that used this method without passing context like this:
context.Response.OnStarting(async () =>
{
await SetSafeStatusCode(context);
});
await _next(context);
I didn't notice any difference while testing second option. Is there any reason for passing the same context that calls this method as a second argument?

The first method allows for the (possibly future) possibility that state and context are indeed different things. Maybe in certain current cases they ARE different things.
It also allows for easier unit testing because the test can directly call OnStarting, without having to construct a complete HttpContext.

Related

HttpContext.Current sometimes lost during async Entity Framework 6 query. Why the difference between the call stacks?

I'm debugging some code and noticed that occasionally, when it accesses the HttpContext.Current it is null and resorts to a fallback that was put in to handle that. The code is an async Web API method that calls down through the application layers and ultimately executes an async Entity Framework 6 query. (Code below) None of the layers in between do anything other than await [method call] - no .ConfigureAwait(false) or anything else.
The project has a System.Data.Entity.Infrastructure.IDbConnectionInterceptor which sets up the SQL session (for SQL RLS) in the Opened method. It uses an injected dependency which, in this case, gets the an ID it needs from the HttpContext.Current.Items collection. When I'm debugging, 95% of the time it works every time, but once in a while I found that HttpContext.Current and SynchronizationContext.Current are both null.
Looking at the Call Stack window, I can see they are arriving at the IDbConnectionInterceptor.Opened method in different ways. The successful version leads back down to my calling code in the Web API controller, but the version where it is null, leads back down to native code. I thought well maybe when it's not null, it's not even executing on a different thread, but Open does execute on a different thread from the original in both cases. My project is targeting .NET Framework 4.8 and referencing the Microsoft.AspNet.WebApi v5.2.3 nuget package. It has <httpRuntime targetFramework="4.7.2" /> under <system.web> in the config file (which I'm just now noticing does not match the 4.8 of the framework). My understanding is that as of .NET Framework 4.5, the context should flow across async calls so it seems like something is preventing that, or somehow Opened is getting queued on a thread that's not using the async/await model. So can someone help me understand the call stack of the failed request, why it might be different from the one that succeeds, and hopefully how that might explain the missing context?
Web API method:
[HttpGet]
[Infrastructure.Filters.AjaxOnly]
[Route("event/month/list/{year}")]
public async Task<IHttpActionResult> GetRoster___EventMonthItems(int year)
{
try
{
HttpContext.Current.SetCallContextFacilityID(); //This extension method sets the mentioned fallback value for when HttpContext.Current is null
List<RosterDayListItem> data = await _roster___Mapper.GetRoster___EventMonthItems(year);
return Ok(data);
}
catch (Exception ex)
{
Logging.DefaultLogger.Error(ex, "An error occurred while loading report categories.");
return BadRequest(ex.Message);
}
}
EF6 Query
public async Task<List<Roster___EventListItem>> GetRoster___EventListItems(int year, int month)
{
using (var dbContextScope = _dbContextFactory.Create())
{
var context = GetContext(dbContextScope);
var result = await context.DropInEvents
.Where(w => w.EventDate.Year == year && w.EventDate.Month == month && w.IsDeleted == false)
.Select(d => new Roster___EventListItem
{
ID = d.ID,
EventDate = d.EventDate,
EventTime = d.StartTime,
Year = d.EventDate.Year
})
.OrderBy(f => f.EventDate).ThenBy(f => f.EventTime)
.ThenByDescending(f => f.EventDate)
.ToListAsync();
return result;
}
}
Successful call stack:
Call Stack with null contexts:
Update
Grasping at straws but after thinking about it for a while, it seemed like maybe something inside EF 6 is perhaps queueing the call to IDbConnectionInterceptor.Opened on a thread in a way that loses the SynchronizationContext. So I went looking through the EF source following my successful stack trace and it looks like the call to Opened is initiated here in InternalDispatcher.DispatchAsync<TTarget, TInterceptionContext> line 257. I'm not sure how it would explain the intermittency of my problem, but might it have something to do with Task.ContinueWith that is being used here? Interestingly I found this other question related to both Task.ContinueWith that method and a SynchronizationContext being lost. Then i found this question where the answer says it will continue with a ThreadPool thread which will not have an associated SyncrhonizationContext unless one is explicitly specified. So this sounds like what I came looking for, but I'm not sure whether the TaskContinuationOptions.ExecuteSynchronously option used changes anything, and if this is the culprit, I don't yet understand why my HttpContext is available most of the time.

How to keep a consistent identifier on an async method with Serilog

Consider the following code:
public async Task DoStuff() {
ILogger logger = LoggerFactory.CreateLogger<PuppetFactory>();
logger.LogDebug("1");
await Something()
logger.LogDebug("2");
await SomethingElse()
logger.LogDebug("3");
}
In the outputTemplate for Serilog, I have {ThreadId}. Of course, since async/await throws the code execution from thread to thread, my log shows different thread IDs.
What can I use in my output template to have the same identifier for this specific execution run?
You'll need to invent and apply the identifier yourself, e.g. OperationId:
// .Enrich.FromLogContext(), then
using (LogContext.PushProperty("OperationId", 123))
{
// Your async code here
}
If the code is literally as above and you can pass the logger along, you can skip the normal LogContext / Enrich.FromLogContext as per Nick's answer and add a contextual property into its context, i.e.
var opLogger = logger.ForContext("OperationId",123);
then use opLogger where you want messages to be tagged - then either use {OperationId} specific property, or the catch-all {Properties} meta-token (which means "all properties not specifically mentioned elsewhere") to emit the value in your message template when rendering.

Overriding Web API ApiControllerActionInvoker Causes Methods to run twice when exceptions thrown

I recently ran into a problem where I was developing an API which talked to two data sources in some methods. The POST for a couple methods modified SQL data through the use of entity framework as well a data source using as an old SDK that was STA COM based. To get the STA COM SDK code to work correctly from within the API methods, I had to create method attributes that identified the methods as needing to be single threaded. I forced single threading by overriding the InvokeActionAsync() method from ApiControllerActionInvoker. If a method was not given an attribute to be single threaded, the overridden invoker simply used the normal base class InvokeActionAsync().
public class SmartHttpActionInvoker: ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext context, CancellationToken cancellationToken)
{
// Determine whether action has attribute UseStaThread
bool useStaThread = context.ActionDescriptor.GetCustomAttributes<UseStaThreadAttribute>().Any();
// If it doesn't, simply return the result of the base method
if (!useStaThread)
{
return base.InvokeActionAsync(context, cancellationToken);
}
// Otherwise, create an single thread and then call the base method
Task<HttpResponseMessage> responseTask = Task.Factory.StartNewSta(() => base.InvokeActionAsync(context, cancellationToken).Result);
return responseTask;
}
}
public static class TaskFactoryExtensions
{
private static readonly TaskScheduler _staScheduler = new StaTaskScheduler(numberOfThreads: 1);
public static Task<TResult> StartNewSta<TResult>(this TaskFactory factory, Func<TResult> action)
{
return factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, _staScheduler);
}
}
public static void Register(HttpConfiguration config)
{
....
config.Services.Replace(typeof(IHttpActionInvoker), new SmartHttpActionInvoker());
...
}
This worked well until I noticed something odd. My Logging database was logging duplicate records when a method NOT marked as single threaded was throwing a HttpResponseException back to the client. This behavior did not exist when the same method returned OK().
Debugging, I noticed the code execute in the API method, then reach the throw statement. The next line after the exception was thrown to be shown in debugger was the InvokeActionAsync() code I wrote. Following this the method was run again, in full, hitting the thrown exception, the action invoker, and then returning the result to the client. Effectively, it appears my use of overriding the InvokeActionAsync causes the Action invoker to be called twice somehow... but I am not sure how.
EDIT: Confirmed that the System.Threading.Thread.CurrentThread.ManagedThreadId for the current thread when it is thrown and logged is different for each execution of the API method. So, this reinforces my belief two threads are being created instead of one. Still not sure why.
Anyone have any experience with overriding the InvokeActionAsync behavior that might be able to explain this behavior? Thanks!

HttpContext.Current is null inside Identity Framework's methods

I am using ASP.NET MVC 5 and Identity Framework. When I call UserManager.UpdateAsync(...) my eventhandlers on ApplicationDbContext() SaveChanges will run. Here I am using HttpContext.Current for different purposes (logging and auditing) so I must get say current user. However the whole method runs in a worker thread, and here HttpContext.Current is null.
The biggest problem that the UserManager's "sync" methods are only wrappers around the async version, so the calls are serialized, but the methods (and eventhandlers) still run in a different worker thread.
Please note this issue has nothing to do with the async/await context. In the controller after the await (or calling the 'sync' version) I have back the correct HttpContext, even the controller's method is continuing in an other thread. That's fine.
So the problem is inside the async worker which will run in both the "sync" and async versions. I think I am understanding the phenomena (but I am not happy with the fake 'sync' method versions, real sync methods would not exhibit this issue.) I just does not know how to deal/workaround it.
[btw: Would not it be more natural to implement UserManager's operarations as simple pure sync versions, then wrap them by async multithreaded wrappers?. IF we continue this async fashion without thinking we will soon invent the async assignment operator. It costs me dozens of hours (just this issue), and costs worldwide zillion dollars, I am sure in many cases less return than its price.]
Bonus: We are talking about UserManager which's impact pretty marginal, but the same principles and issues can apply any out of the box library (black box for you) which authors do not implement sync versions and or do not care about the controller thread's context. What about EF, it is not so marginal... and what about DI containers instantiation infrastructure like "request scope" or "session scope". Surely they misbehave if resolving occurs in a thread with no HttpContext.Current. Recently I refreshed SendGrid NuGet, and (as a breaking change) Deliver() method gone, and now only DeliverAsync() is existing...
I would like to have a safe reliable way, how can I access the HttpContext inside this worker for logging and audit purposes.
Sample code, the controller 'sync' version:
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Edit(ApplicationUser user)
{
// validation etc
// Update() seems to be only a poor wrapper around the async version, still uses a worker thread.
var result = UserManager.Update(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
Sample code, the controller async version:
[AcceptVerbs(HttpVerbs.Post)]
public virtual async Task<ActionResult> Edit(ApplicationUser user)
{
// validation etc
var result = await UserManager.UpdateAsync(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
and the event handler where HttpContext is null:
public ApplicationDbContext() : base("DefaultConnection", false)
{
InitializeAudit();
}
private void InitializeAudit()
{
var octx = ((IObjectContextAdapter) this).ObjectContext;
octx.SavingChanges +=
(sender, args) =>
{
// HttpContext.Current is null here
};
}
Any ideas?
As you said, this occurs because of threading. The delegate runs in a different thread, making the HttpContext inaccessible.
You can move the variable outside of the delegate, making it a closure.
private void InitializeAudit()
{
var octx = ((IObjectContextAdapter) this).ObjectContext;
HttpContext context = HttpContext.Current;
octx.SavingChanges +=
(sender, args) =>
{
// context is not null
};
}
You are using asp.net identity through owin,
so one instance of the dbcontext is created per request,
and you can get this reference from anywhere in the request pipeline.
nb. this is handy but i think the dbcontext shouldn't be accessed outside the manager.
In asp.net identity design, only the manager should be aware of the store.
I believe the dbcontext is exposed because several asp.net identity middleware have a dependance on it.
But, it could help resolve you problem:
Allow your custom dbcontext handler to be set outside the class:
public EventHandler SavingChangesEventHandler
{
set
{
(((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext).SavingChanges += value;
}
}
Declare a custom ActionFilter class and register it, then override OnActionExecuting:
Filtering in ASP.NET MVC
https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx
public class CustomizeAppDbcontextFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var dbcontext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
var currentuser = HttpContext.Current.User;
dbcontext.SavingChangesEventHandler = (sender, args) =>
{
// use currentuser
};
}
}
you may need these using statements to be able to call the identity.owin extension methods:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
You should be in the controller thread because OnActionExecuting is wrapping the controller action.
I did not test it, so it may need some polishing but the concept should work.

HttpContext.Current is null after await (only in unit tests )

I am writing unit test for MVC 5 web application. I have mocked the HttpContext.Current from my test.
When run following code form the test httpSessionStateAfter throw
System.AggregateException : One or more errors occurred.
----> System.NullReferenceException : Object reference not set to an instance of an object.
This happen only when i run the unit tests. When application run this work fine.
I'm using Nunit 2.6.3 with reshaper test runner.
var httpSessionStateBefour = System.Web.HttpContext.Current.Session;
var Person= await Db.Persons.FirstOrDefaultAsync();
var httpSessionStateAfter = System.Web.HttpContext.Current.Session;
How to over come this problem?
This is how i mock the HttpContext
HttpContext.Current = Fakes.FakeHttpContext();
HttpContext.Current.Session.Add("IsUserSiteAdmin", true);
HttpContext.Current.Session.Add("CurrentSite", null);
public static class Fakes
{
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://stackoverflow/", "");
var stringWriter = new StringWriter();
var httpResponce = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof (HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] {typeof (HttpSessionStateContainer)},
null)
.Invoke(new object[] {sessionContainer});
return httpContext;
}
}
First, I do recommend that you isolate your code as much as possible from HttpContext.Current; not only will this make your code more testable, but it will help prepare you for ASP.NET vNext, which is more OWIN-like (without an HttpContext.Current).
However, that can require a lot of changes, which you may not yet be ready for. To properly mock HttpContext.Current, you need to understand how it works.
HttpContext.Current is a per-thread variable that is controlled by the ASP.NET SynchronizationContext. This SynchronizationContext is a "request context", representing the current request; it's created by ASP.NET when a new request comes in. I have an MSDN article on SynchronizationContext if you're interested in more details.
As I explain in my async intro blog post, when you await a Task, by default it will capture the current "context" and use that to resume the async method. When an async method is running within an ASP.NET request context, the "context" captured by the await is the ASP.NET SynchronizationContext. When the async method resumes (possibly on a different thread), the ASP.NET SynchronizationContext will set HttpContext.Current before resuming the async method. This is how async/await works within an ASP.NET host.
Now, when you run the same code in a unit test, the behavior is different. Specifically, there is no ASP.NET SynchronizationContext to set HttpContext.Current. I'm assuming that your unit test method returns Task, in which case NUnit does not provide a SynchronizationContext at all. So, when the async method resumes (possibly on a different thread), its HttpContext.Current may not be the same one.
There's a few different ways to fix this. One option is to write your own SynchronizationContext that preserves HttpContext.Current, just like the ASP.NET one does. An easier (but less efficient) option is to use a SynchronizationContext that I wrote called AsyncContext, which ensures the async method will resume on the same thread. You should be able to install my AsyncEx library from NuGet and then wrap your unit test methods inside a call to AsyncContext.Run. Note that the unit test methods are now synchronous:
[Test]
public void MyTest()
{
AsyncContext.Run(async () =>
{
// Test logic goes here: set HttpContext.Current, etc.
});
}
HttpContext.Current is considered a pretty terrible property to work with; it doesn't behave itself outside of its ASP.NET home. The best way to fix your code is to stop looking at this property and find a way to isolate it from the code you are testing. For example, you could create an interface which represents your current session's data and expose that interface to the component you are testing, with an implementation that requires an HTTP context.
The root problem is to do with how the HttpContext.Current works. This property is "magical" in the ASP.NET framework, in that it is unique for a request-response operation, but jumps between threads as the execution requires it to - it is selectively shared between threads.
When you use HttpContext.Current outside of the ASP.NET processing pipeline, the magic goes away. When you switch threads like you are here with the asynchronous programming style, the property is null after continuing.
If you absolutely cannot change your code to remove the hard dependency on HttpContext.Current, you can cheat this test by leveraging your local context: all the variables in local scope when you declare a continuation are made available for the context of the continuation.
// Bring the current value into local scope.
var context = System.Web.HttpContext.Current;
var httpSessionStateBefore = context.Session;
var person = await Db.Persons.FirstOrDefaultAsync();
var httpSessionStateAfter = context.Session;
To be clear, this will only work for your current scenario. If you introduce an await ahead of this in another scope, the code will suddenly break again; this is the quick-and-dirty answer that I encourage you to ignore and pursue a more robust solution.
I came to this question when having an issue in my code... where the HTTPContext.Current is null after an Await in an Async MVC Action. I post this here because others like me might land here.
My general recommendation is to grab anything you want off the session into a local variable, much like others above discuss, but not worrying about keeping the context, and instead just worrying about grabbing the actual items you want.
public async Task<ActionResult> SomeAction(SomeModel model)
{
int id = (int)HttpContext.Current.Session["Id"];
/* Session Exists Here */
var somethingElseAsyncModel = await GetSomethingElseAsync(model);
/* Session is Null Here */
// Do something with id, thanks to the fact we got it when we could
}

Categories

Resources