调用任务函数发送电子邮件时未将对象引用设置为对象的实例

问题描述

我在控制器中有以下代码

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);
        }

当我调用 SendEmail 函数时,出现以下中断错误

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,Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,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]

解决方法

您看到的问题是由请求外部代码引起的。所有请求外部代码都是危险的。

具体来说,代码正在调用 SendEmail 但没有 await 对结果进行处理。所以它试图做一种“即发即忘”。在它调用 SendEmail 之后,它将响应发送回客户端,让 SendEmail 作为请求外部代码运行。

然而,SendEmail 是在 ASP.NET 请求上下文中调用的,因此当它尝试在 await 之后继续执行时,会遇到错误,因为该 ASP.NET 请求上下文已经消失.响应已经发送,所以没有更多的 ASP.NET 请求上下文。

最好的解决方案是删除请求外部代码。 await 调用 SendEmailbuild a basic distributed architecture for asynchronous messaging。我只想说使用 await 除非你必须提前返回,在这种情况下你需要一个持久队列和一个工作进程。 MVC 操作会将包含所有电子邮件详细信息的消息写入队列,然后返回 HTTP 结果。工作进程将读取该队列并发送实际的电子邮件。

任何其他解决方案都将使用 request-extrinsic code,which is inherently dangerous。具体来说,任何请求外部代码都可能在没有警告和日志的情况下停止工作。 “即发即忘”字面意思的意思是“忘记”。工作(在这种情况下是电子邮件)可能会丢失。但是,如果您对此没有意见,那么您可以使用 NoContext from my AsyncEx library 之类的东西以不引用 ASP.NET 请求上下文的方式调用请求外部代码。