I have the following code in a controller.
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private ApplicationDbContext userDB;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
public async Task SendEmail(string user_id, string subject, string user_email )
{
await UserManager.SendEmailAsync(user_id, subject, user_email);
}
public ActionResult Transfer(int id, int nurse_id, int ambulance_id, int driver_id, int nurse_account_id)
{
...
/////////////////////////////////
//Send email
string nurse_user_id = db.Nurse_Account.Where(m => m.nurse_account_id == call.nurse_account_id).Select(m => m.nurse_account_user_id).First();
string usr = db.AspNetUsers.Where(m => m.Id == nurse_user_id).Select(m => m.Id).First();
string usr_mail = db.AspNetUsers.Where(m => m.Id == nurse_user_id).Select(m => m.Email).First();
SendEmail(usr, "New call", usr_mail);
////////////////////////////////
...
return Json(new { result = "OK" }, JsonRequestBehavior.AllowGet);
}
When I'm calling SendEmail
function I get the following break error:
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=mscorlib
StackTrace:
at System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAction(Object state)
at System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask)
at System.Threading.Tasks.AwaitTaskContinuation.<>c.<ThrowAsyncIfNecessary>b__18_0(Object s)
at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
This exception was originally thrown at this call stack:
[External Code]
The problem that you're seeing is due to request-extrinsic code. All request-extrinsic code is dangerous.
Specifically, the code is calling SendEmail
but not await
ing the result. So it's trying to do a kind of "fire-and-forget". After it calls SendEmail
then it sends the response back to the client, leaving SendEmail
running as request-extrinsic code.
However, SendEmail
is called within the ASP.NET request context, and so when it tries to resume executing after its await
, it runs into the error because that ASP.NET request context is gone. The response has already been sent, so there's no more ASP.NET request context.
The best solution is to remove the request-extrinsic code. Either await
the call to SendEmail
or build a basic distributed architecture for asynchronous messaging. I would say just to use the await
unless you must return early, in which case you need a durable queue and a worker process. The MVC action would write a message into the queue including all the email details, and then it would return the HTTP result. The worker process would read that queue and send the actual email.
Any other solution would use request-extrinsic code, which is inherently dangerous. Specifically, any request-extrinsic code may stop working without warning and without logs. "Fire and forget" literally means "forget". It's possible that work (emails in this case) may be lost. But if you're OK with that, then you can call the request-extrinsic code in a way that it does not reference the ASP.NET request context by using something like NoContext
from my AsyncEx library.
User contributions licensed under CC BY-SA 3.0