How can I call an async method from Session_Start
in Global.asax ?
Global.asax:
protected async Task Session_Start(object sender, EventArgs e)
{
Session.Timeout = 10;
// Do some asynch work
await repository.SetStatsInfo(System.DateTime.Now);
}
async method:
public async Task SetStatsInfo(DateTime time)
{
using (ApplicationDBContext db = new ApplicationDBContext())
{
// Do stuff (update visitors counter in db) ..
await db.SaveChangesAsync();
}
}
I can run it all synchronously (define void Session_Start
etc.) which is working, but would prefer the async way so that hitting the db is not blocking.
Running like this with 'async Task' for Session_Start
, the code is not executed,
breakpoints inside session_start
are not hit.
async and await Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.
The first thing to do is to add the async keyword to Action Method. If we use the async Keyword in Method, the Method must also use await Keyword. The return type of an async method must be void, Task or Task<T> we have used Task<T> in Action Method.
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
From what I understand, ASP has a designated thread that is the ONLY thread that has access to the HttpContext.Current
object, and in turn, access to the Session
(HttpContext.Current.Session
) much like the UI thread in windows applications. Therefore, performing a .Wait()
or .Result
in Session_Start callback will give you unknown results and/or deadlock the process.
There seems to be numerous ways to govern a Task's thread of execution, the primary one being specifying your task to run with a certain synchronization context via the TaskScheduler class https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v=vs.110).aspx#Sync
However, since the Task class was designed to implement IAsyncResult
, which comes from the APM (Asynchronous Programming Model) pattern, this makes Task backwards compatible with older APM pattern code (Which from what I understand ASP was originally built on). It takes a bit of integration work, though https://blogs.msdn.microsoft.com/mazhou/2011/10/04/the-asynchronous-programming-models/ (Standard APM).
.Net 4.5 introduced a nice Task wrapper (EventHandlerTaskAsyncHelper
) to perform asynchronous actions utilizing the APM-style HttpApplication
was built to support. It meets all the requirements to access the Session object, and execute correctly in the HttpApplication
:
public override void Init()
{
base.Init();
//EventHandlerTaskAsyncHelper Wraps the task call in an APM-style BeginEventHandler, EndEventHandler
var wrapper = new EventHandlerTaskAsyncHelper(AsyncSessionStart);
this.AddOnAcquireRequestStateAsync(wrapper.BeginEventHandler, wrapper.EndEventHandler);
}
private async Task AsyncSessionStart(Object sender, EventArgs evtArgs)
{
//The only caveat is we have to check IsNewSession to see if it was created in this call
//This doesn't need to be applied for other AddOn*Async wire-ups
if (!Session.IsNewSession)
return;
await doSomethingAsync();
}
//I recall seeing something that for session state to be active, this callback has to be declared, even if empty
protected void Session_Start(object sender, EventArgs e)
{
//Synchronous session
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With