我有一个Controller Action方法来保存用户详细信息,如下所示。
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
return Json(new { Success = String.IsNullOrEmpty(result) });
}
到目前为止,上述4行函数没有问题。
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
new MailController().CreateUser(user.userDetails); //<==This is problem line of code.
}
以下是我的邮件控制器。
public class MailController : MailerBase
{
public void CreateUser(ViewModel.VM_User user)
{
To.Add(user.EmailAddress);
From = System.Configuration.ConfigurationManager.AppSettings["emailSender"];
Subject = "Hi";
Email("CreateUser", user).DeliverAsync();
}
}
在上面的代码中,我在执行电子邮件发送代码时遇到问题。我收到以下错误消息。
异步模块或处理程序在异步时完成 操作仍在等待
请提出纠正措施!!
答案 0 :(得分:4)
这是因为async
方法跟踪其完成情况,甚至是async void
。他们通过在启动时注册SynchronizationContext
并在返回时标记操作完成来完成此操作。 ASP.NET跟踪所有创建的操作,并要求在操作返回之前完成所有操作,否则它会向HTTP客户端返回错误。如果您需要运行“fire and forget”方法,则必须手动避免在ASP.NET SynchronizationContext
上启动它。例如,await Task.Run(() => CallFireAndForget())
(await
以便您等待同步部分在线程池上运行。这是因为CallFireAndForget()
是async void
。要触发返回的方法Task
即发消失,您必须明确避免将Task
返回Task.Run()
,如下所示:await Task.Run(() => { CallFireAndForgetAsync(); })
)。
要显示相同错误消息的另一种方法应该是在异步操作中编写它:
// BAD CODE, DEMONSTRATION ONLY!
AsyncOperationManager.CreateOperation();
如果您使用AsyncOperation
API,则必须确保在允许操作方法返回之前调用AsyncOperation.OperationCompleted()
。
在您的情况下,如果邮件发送确实是一个即席即忘的任务,您可以在将其签名更改为CreateUser
之后在async Task CreateUserAsync(ViewModel.VM_User user)
内部执行以下操作:
await Task.Run(() => Email("CreateUser", user).DeliverAsync());
并在你的行动中:
await new MailController().CreateUserAsync(user.userDetails);
理想情况下,您不会将Task.Run()
用于此类内容。相反,您可以暂时替换当前的SynchronizationContext
:
var originalSynchronizationContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
new MailController().CreateUser(user.userDetails);
}
finally
{
SynchronizationContext.SetSynchronizationContext(originalSynchronizationContext);
}
答案 1 :(得分:2)
DeliverAsync是否返回任务?如果是的话,试试这个。
public class MailController : MailerBase
{
public async Task CreateUserAsync(ViewModel.VM_User user)
{
To.Add(user.EmailAddress);
From = System.Configuration.ConfigurationManager.AppSettings["emailSender"];
Subject = "Hi";
await (Email("CreateUser", user).DeliverAsync());
}
}
然后在您的控制器中,等待CreateUserAsync返回的任务。
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
await (new MailController().CreateUserAsync(user.userDetails));
return Json(new { Success = String.IsNullOrEmpty(result) });
}
注意:如果你的目标是让电子邮件发送背景并忘记操作,那就不是了。