错误:在异步操作仍处于挂起状态时完成异步模块或处理程序

时间:2015-08-29 15:58:05

标签: c# asp.net-mvc asp.net-mvc-3 asp.net-mvc-4

我有一个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();
    }
}

在上面的代码中,我在执行电子邮件发送代码时遇到问题。我收到以下错误消息。

  

异步模块或处理程序在异步时完成   操作仍在等待

请提出纠正措施!!

2 个答案:

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

注意:如果你的目标是让电子邮件发送背景并忘记操作,那就不是了。