Advertisement

#4 Sending Email Using Mailkit

In this article we will create the ability to send emails from our web application using the Mailkit nuget package. In addition to sending basic emails our message service will also be able to deal with the cases when we want to send along attachments as well.

Installing Mailkit

We will start by installing the Mailkit nuget package that we will enable us to send emails from our application (a).

Image showing the Mailkit package version number at the time of the writing of this article.
(a) Image showing the Mailkit package version number at the time of the writing of this article.

Sometimes we want to send an attachment

  • WebUi
    • Features
      • Messaging
        • Attachment.cs

Although strictly speaking now required to start with we will go ahead and deal with the case where we want to send an attachment with an email. To do this we start by defining an attachment class (b). In addition to simply holding the information that we want to send in the Content property we also have the ability to convert the content to a memory stream using the ContentToStreamAsync method (b).

Attachment.cs

using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace WebUi.Features.Messaging
{
    public class Attachment
    {
        public Attachment(string fileName, object content)
        {
            Content = content;
            FileName = fileName;
            Type = AttachmentType.Text;
        }

        public enum AttachmentType
        {
            Json,
            Text
        }

        public object Content { get; set; }
        public string FileName { get; set; }
        public AttachmentType Type { get; set; }

        public async Task<MemoryStream> ContentToStreamAsync()
        {
            string text;
            switch (Type)
            {
                case AttachmentType.Json:
                    text = Newtonsoft.Json.JsonConvert.SerializeObject(Content);
                    break;
                case AttachmentType.Text:
                    text = Content.ToString();
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            // Would create a string extension method to be able to reuse the following.
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream, Encoding.UTF8);
            await writer.WriteAsync(text);
            await writer.FlushAsync();
            stream.Position = 0;
            return stream;
        }
    }
}
(b) Our attachment class will take care of holding the data that we want to attach to our email as well as convert it to a memory stream that will allow us to actually attach it to our email.

Time for our message service and its interface

  • WebUi
    • Features
      • Messaging
        • IMessageService.cs

We will start by defining an interface that our message service will implement. The interface, at least for right now, will contain a single method signature shown in (c).

IMessageService.cs

using System.Threading.Tasks;

namespace WebUi.Features.Messaging
{
    public interface IMessageService
    {
        Task SendEmailAsync(
            string fromDisplayName, 
            string fromEmailAddress, 
            string toName, 
            string toEmailAddress, 
            string subject, 
            string message, 
            params Attachment[] attachments);
    }
}
(c) The interface for our message service that specifies the fundamental method of sending an email.
Advertisement
  • WebUi
    • Features
      • Messaging
        • MessageService.cs

Of course an interface is not very useful without a class that implements it which brings us to our message service (d). Most of the code below is of course not specific to any situation except for the area marked as specific to a particular provider. You will need to supply the information for your particular situation.

MessageService.cs

using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MimeKit;

namespace WebUi.Features.Messaging
{
    public class MessageService : IMessageService
    {
        public async Task SendEmailAsync(
            string fromDisplayName, 
            string fromEmailAddress, 
            string toName, 
            string toEmailAddress, 
            string subject, 
            string message, 
            params Attachment[] attachments)
        {
            var email = new MimeMessage();
            email.From.Add(new MailboxAddress(fromDisplayName, fromEmailAddress));
            email.To.Add(new MailboxAddress(toName, toEmailAddress));
            email.Subject = subject;

            var body = new BodyBuilder
            {
                HtmlBody = message
            };
            foreach (var attachment in attachments)
            {
                using (var stream = await attachment.ContentToStreamAsync())
                {
                    body.Attachments.Add(attachment.FileName, stream);
                }
            }

            email.Body = body.ToMessageBody();

            using (var client = new SmtpClient())
            {
                client.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                // Start of provider specific settings
                await client.ConnectAsync("smtp.host", 587, false).ConfigureAwait(false);
                await client.AuthenticateAsync("username", "password").ConfigureAwait(false);
                // End of provider specific settings

                await client.SendAsync(email).ConfigureAwait(false);
                await client.DisconnectAsync(true).ConfigureAwait(false);
            }
        }
    }
}
(d) Our message service class that will be responsible for taking in the required information and sending out a corresponding email.

The final touch is setting up dependency injection

  • WebUi
    • Startup.cs

At some point we might get around to doing what we are supposed to be doing and that is testing everything and we all know that to support that we should be injecting our services into the classes that need the functionality. To do this we of course just need to add a reference to both our message service interface and the concrete implementation within the configure services method of our startup class (e).

Startup.cs

...
using WebUi.Features.Messaging;
...
namespace WebUi
{
    public class Startup
    {
        ...
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            services.AddTransient<IMessageService, MessageService>();
        }
    }
}
(e) Configuring our concrete message service to be injected whenever we ask for a class that implements the interface.
Exciton Interactive LLC
Advertisement