Calling an asynchronous function in an Actionresult?

Asked

Viewed 1,975 times

4

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

I have a form that when saving it needs to store data in the database and simultaneously send an email notifying a user that such action has been done in the system. But I want the sending of email to be executed asynchronous of the method that saves the data.

I have used as a basis an ex taken from the net, but in it 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);
        }
    } 
  • But is there any reason why Action isn’t asynchronous? And your question is how to perform the tasks in parallel (each in a thread, to be executed "at the same time") or to execute the task as async, not to lock the Thread while running the upload?

  • @Diegojeronymo Exactly this second option. Because when the user clicks on the Submit button of the form, it gives a delay, because currently the sending of email is not asynchronous.

1 answer

4


First of all, it should be clear that the use of async/await in Asp.Net applications has the purpose of only releasing the request threads during blocking operations (I/O, external requests, database queries, etc.) the server has for its applications.

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

Having clarified these points, the best way to call your asynchronous method is to make your action 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 and Savechanges() will release the request thread during its execution. With the use of await in both, the execution flow remains similar to that of a synchronous method, but with the advantages I cited of async/await.

But I want the sending of email to be executed asynchronous of the method that saves the data.

If your idea is to have it send without waiting for it to actually send to run the recording in the bank, 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);
    }
}

This way, the application will go through the method, sending the email, but leaving through the return of Task the choice for who called him to expect or not his execution.

The question of how your Controller would look depends on your intention:

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");
}
  • It couldn’t be clearer! Sanou all the doubts that had async/await methods in a practical and clear way! Obrg.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.