Home > database >  Why do I need to use Wait when Sending email using Sendgrid C# client library in a console app
Why do I need to use Wait when Sending email using Sendgrid C# client library in a console app

Time:05-17

I am using the C# client library for Sendgrid version 9.27.0.

The only way I can get it to send an email is by adding Wait() to end of my method.

I understand the await operator is a promise to return back to the point in code after the asynchronous method is finished.

But why do I need to add Wait()? Isn't that converting the asynchronous method to synchronous? If so, what's the point in making it async?

Program.cs

 static void Main(string[] args) {
        //var customerImport = new CustomerImport();
        //customerImport.DoImport();

        var mailClient = new MailClient();
        var recipients = new List<string>();
        recipients.Add("[email protected]");

        //Never sends an email
        var response = mailClient.SendMail("[email protected]", recipients, "Test email", "This is a test of the new SG client", false);

        //Will send an email
        mailClient.SendMail("[email protected]", recipients, "Test email", "This is a test of the new SG client", false).Wait();
        

    }

MailClient.cs

public async Task SendMail(string emailFrom, List<string> emailTo, string subject, string body, bool isPlainText) {

        try {
            var apiKey = Utils.GetConfigValue("sendgridAPIKey");
            var emails = new List<EmailAddress>();

            foreach (string email in emailTo) {
                emails.Add(new EmailAddress(email));
            }

            var plainTextContent = "";
            var htmlContent = "";

            if (!isPlainText) {
                htmlContent = body;
            } else {
                plainTextContent = body;
            }

            var message = MailHelper.CreateSingleEmailToMultipleRecipients(new EmailAddress(emailFrom, "LobbyCentral"), emails, subject, plainTextContent, htmlContent);

            //if (metaData != null)
            //    message.AddCustomArgs(metaData);

            foreach (string filename in FileAttachments) {
                if (System.IO.File.Exists(filename)) {
                    using (var filestream = System.IO.File.OpenRead(filename)) {
                        await message.AddAttachmentAsync(filename, filestream);
                    }
                }
            }

            foreach (PlainTextAttachmentM plainTextM in PlainTextAttachments) {
                byte[] byteData = Encoding.ASCII.GetBytes(plainTextM.Content);

                var attachment = new Attachment();
                attachment.Content = Convert.ToBase64String(byteData);
                attachment.Filename = plainTextM.AttachmentFilename;
                attachment.Type = "txt/plain";
                attachment.Disposition = "attachment";

                message.AddAttachment(attachment);
            }
            
            var client = new SendGridClient(apiKey);
            var response = await client.SendEmailAsync(message);

            if (response.IsSuccessStatusCode) {

                if (DeleteAttachmentsAfterSend && FileAttachments.Count > 0) {
                    foreach (string filename in FileAttachments) {
                        if (System.IO.File.Exists(filename)) {
                            System.IO.File.Delete(filename);
                        }
                    }
                }
            } else {
                Utils.DebugPrint("error sending email");
            }


        } catch (Exception ex) {
            throw new Exception(string.Format("{0}.{1}: {2} {3}", System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message, ex.StackTrace));
        }
    }

CodePudding user response:

You need to await it in this console app because the very last thing that the Main() does before it returns (and the app exits) is send the mail. I'd say there is slim to no chance at all that the mail sending task will complete before the app exits; it involves a lot of IO, whereas it will take nanoseconds for the app to quit, taking the incomplete Task with it

Anything you do to make it wait long enough will suffice; turning it synchronous with a call to Wait, making the Main as async Task Main(...), even adding a Console.ReadLine will all mean the mail sending will have time to complete ..

.. though I question why a console app that only does one thing then exits even needs to be async anything - it simply has no other jobs to do while it waits for this IO to complete so there seems little point using any asyncronous facilities

CodePudding user response:

Calling mailClient.SendMail starts a Task, but doesn't wait for its completion.

If that's the last instruction of you program, the program simply ends before the tasks can finish.

You want your last instructions to start and wait for the task's completion. You can do that by using either of the following.

Make your Main async. (That's what I would personally do.)

// Signature changed to async Task instead of void.
static async Task Main(string[] args) {
        // (...)
        
        // Added await. Removed Wait.
        await mailClient.SendMail("[email protected]", recipients, "Test email", "This is a test of the new SG client", false);
}

Use Wait like you're doing.

static void Main(string[] args) {
        // (...)
        
        var task = mailClient.SendMail("[email protected]", recipients, "Test email", "This is a test of the new SG client", false);
        task.Wait();
}
  • Related