Call an asynchronous function in an ActionResult?

4

How to call an asynchronous function on a non-asynchronous controller, to be clearer follows a situation:

I have a form that when saving it needs to save data in the database and simultaneously send an email notifying a user that such action has been taken on the system. But I want the sending of emails to be executed asynchronously from the method that writes the data.

I used as base an ex removed from the net, but in it only shows when the method is called directly and not when it is called another non-asynchronous method.

Code:

[HttpPost]
    public ActionResult Index(DuvidasForm form)
    {
        if (!ModelState.IsValid)
            return View(form);

        var duvida = new Duvida 
        {
            nome_envio = form.nome_envio,
            email_envio = form.email_envio,
            assunto_envio = form.assunto_envio,
            msg_envio = form.msg_envio,
            data_envio = DateTime.Now,
            cursoid = form.cursoid,
            temaid = form.temaid,
            respondida = false
        };

        EnviarEmail(form.email_envio, form.assunto_envio, form.msg_envio);

        db.Duvida.Add(duvida);
        db.SaveChanges();

        return PartialView("_NotificacaoEmail");
    }

public async Task EnviarEmail(string toEmailAddress, string emailSubject, string emailMessage)
    {
        var smtp = new SmtpClient();


        var message = new MailMessage();
        message.To.Add(toEmailAddress);

        message.Subject = emailSubject;
        message.Body = emailMessage;

        using (var smtpClient = new SmtpClient())
        {
            await smtpClient.SendMailAsync(message);
        }
    } 
    
asked by anonymous 04.03.2016 / 13:37

1 answer

4

First of all, it should be clear that the use of async / await in Asp.Net applications is intended only to release the request threads during blocking operations (I / O, external requests, queries to databases, etc.) that the server has for your applications.

The result of this is a better use of server resources, which is able to respond to more requests. However, this does not mean that the requests will be faster or that the end user will feel some difference during navigation directly because of this.

Clarifying these points, the best way to call your asynchronous method is to make your Action be asynchronous too:

public async Task<ActionResult> Index(DuvidasForm form)
{
    if (!ModelState.IsValid)
        return View(form);

    var duvida = new Duvida
    {
        nome_envio = form.nome_envio,
        email_envio = form.email_envio,
        assunto_envio = form.assunto_envio,
        msg_envio = form.msg_envio,
        data_envio = DateTime.Now,
        cursoid = form.cursoid,
        temaid = form.temaid,
        respondida = false
    };

    await EnviarEmail(form.email_envio, form.assunto_envio, form.msg_envio);

    db.Duvida.Add(duvida);
    await db.SaveChangesAsync(); // EF também pode ser executado async!

    return PartialView("_NotificacaoEmail");
}

In this way, both e-mail sending and SaveChanges () will release the request thread during execution. With the use of await in both, the execution flow remains similar to that of a synchronous method, but with the advantages I mentioned async / await.

  

But I want to send email asynchronously to the method that writes the data.

If your idea is to have it shipped without expecting it to actually send to run the write to the database, then your method of sending emails should not use the async / await keywords:

public Task EnviarEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
    var smtp = new SmtpClient();


    var message = new MailMessage();
    message.To.Add(toEmailAddress);

    message.Subject = emailSubject;
    message.Body = emailMessage;

    using (var smtpClient = new SmtpClient())
    {
        return smtpClient.SendMailAsync(message);
    }
}

In this way, the application will go directly through the method, sending the email, but leaving the Task return to the choice for who called it to wait or not to execute it. / p>

The question of how your Controller would look depends on what you intend to do:

public async Task<ActionResult> Index(DuvidasForm form)
{
    // o resto continua igual

    var taskEmail = EnviarEmail(form.email_envio, form.assunto_envio, form.msg_envio);

    db.Duvida.Add(duvida);
    await db.SaveChangesAsync();

    // se você quer esperar explicitamente o fim do envio antes de retornar, então use a linha abaixo
    // await taskEmail;

    return PartialView("_NotificacaoEmail");
}
    
09.03.2016 / 04:06