.NET Core Email Support

Home / .NET Core Email Support

Out of the box, .NET Core has no support for email. The System.Net.Mail namespace hasn’t been ported as of .NET Core 1.1.3. Fortunately, there are some nice email libraries that support .NET Core.

The one library that dug into is called MailKit. It’s available from Nuget.

Install-Package MailKit

For the application that I’m currently working, I need to be able to send emails via SMTP and to receive/check emails via IMAP.

After adding MailKit to my project, sending an email via SMTP is pretty straight-forward. It is very similar to the System.Net.Mail client. You connect/authenticate via the SMTP server, build up a MimeMessage object, and then send it.

In the code below, I’m sending a plain-text email although it’s just as easy using the “BodyBuilder” to send HTML. I also made the method signature very long with all parameters for illustration. In my actual code, I’m passing the SMTP server settings via IOptions and a domain object that contains the email details.

public async Task<bool> SendEmailAsync(string username, string password, string smtpHost, int smtpPort,
    string from, string toName, string toEmail, string subject, string body)
{
    MimeMessage message = new MimeMessage();

    message.From.Add(new MailboxAddress("My Special Subject", from));
    message.To.Add(new MailboxAddress(toName, toEmail));
    message.Subject = subject;
    message.Body = new TextPart("plain") { Text = body };
    var useSsl = true;
    try
    {
        using (var client = new SmtpClient())
        {
            if (!useSsl)
            {
                client.ServerCertificateValidationCallback =
                  (object sender2, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => true;
            }

            await client.ConnectAsync(smtpHost, smtpPort, useSsl)
                .ConfigureAwait(false);
            client.AuthenticationMechanisms.Remove("XOAUTH2");

            if (!string.IsNullOrWhiteSpace(username))
            {
                await client.AuthenticateAsync(username, password)
                    .ConfigureAwait(false);
            }

            await client.SendAsync(message).ConfigureAwait(false);
            await client.DisconnectAsync(true).ConfigureAwait(false);
        }

        return true;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

Checking email via IMAP is interesting. The goal I was going for using IMAP was to read unread email with a subject containing a particular text string. I also wanted to mark emails as read after processing them.

MailKit provides a querying mechanism that works against the particular folder/inbox of the authenticated user. This querying mechanism allows searching for emails precisely as I described above. Via the handle to a particular email, MailKit also allows setting the email as read (seen).

public async Task<bool> FetchEmailAsync(string imapHost, int imapPort, string username, string password)
{
    try
    {
        // Try reading messages
        using (var client = new ImapClient())
        {
            // For demo-purposes, accept all SSL certificates
            client.ServerCertificateValidationCallback = (s, c, h, e) => true;

            client.Connect(imapHost, imapPort, true);

            // Renive XOAUTH2 since we are neither using Oauth2 not have an Oauth2 token
            client.AuthenticationMechanisms.Remove("XOAUTH2");

            client.Authenticate(username, password);

            // Our emails are in the Inbox folder.  The Inbox folder is always available on all IMAP servers...
            var inbox = client.Inbox;
            inbox.Open(FolderAccess.ReadWrite);

            // Let's get unread. Note the querying ..
            var searchQuery = new SearchQuery()
                .And(SearchQuery.NotSeen)
                .And(SearchQuery.SubjectContains("My Special Subject"));

            // The query returns unique id's
            var uids = await inbox
                .SearchAsync(searchQuery);

            // Now get all of the messages.
            var messages = uids
                .Select(x =>
                {
                    var message = inbox.GetMessage(x);
                    var subject = message.Subject;
                    var from = message.From;
                    var cc = message.Cc;
                    var replyTo = message.ReplyTo;
                    var to = message.To;

                    try
                    {
                        // Process message
                        Console.WriteLine($"Read message from Exchange. Subject: {subject}");
                        
                        // Set the email as "seen"
                        inbox.AddFlags(uids, MessageFlags.Seen, true);
                    }
                    catch (Exception ex)
                    {
                        // Per message handling?
                    }
                    return message;
                })
                .ToList();                    

            client.Disconnect(true);
            return true;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

On a side note, I have a Quartz scheduler that is running every minute to check my IMAP messages. Sure, this could be handled as a push notification from my IMAP (Exchange) server, but in a Web/Service context, I didn’t want to hold the connection open.

Leave a Reply