Sending Emails in ASP.NET Core with MailKit

In this article, let's see how we can send this HTML content as an Email to users. We'll use MailKit, which is one of the most popular Mail Client lib..

In a previous article, we have seen how we can use RazorViewEngine of ASP.NET Core MVC to produce dynamic HTML content. The goal we’ve achieved is to generate dynamic HTML content from data we obtain from our datastore. You can read the article here – Fetch Rendered HTML in ASP.NET Core

The next step is to broadcast this content to the intended users.

The Usecase

Imagine you’re running a bussines application which sends out newsletters for your recepients. Since you already had the content prepared, the next step is to send it to the users.

One way is to manually type and send emails to each of your recepient. The other is to programmatically send mails to target users through the application. How do you do this? By using an SMTP server and a Mail Client library.

In this article, let’s see how we can send this HTML content as an Email to users. We’ll use MailKit, which is one of the most popular Mail Client libraries for .NET Core to achieve this. It helps us connect to our SMTP server and send out mails while providing us the high-level utilities and features for better drafting our mails.

What is SMTP?

SMTP stands for Simple Mail Transmission Protocol, which is the architecture defined to send electronic-mails or Emails over the internet. Each recepient has a unique identifier to send or receive mails, which we call as email addresses.

How Email Works?

All the popular mail providers such as GMail, YMail, Hotmail / Outlook etc. are internally email hosting servers, through which the system works.

When we send out a mail to some bro@aka.com, it internally understands that the recipient is a person of the identifier ‘bro’ under the email server with the domain ‘aka.com’

The message packets are then sent to the mail server under the domain ‘aka.com’ via the SMTP protocol and internally the mail server at ‘aka.com’ delivers to the specified identifier account.

To send mails, we need a FromAddress, a ToAddress and a MailServer.

To send mails programmatically, we require a mail server and a recipient address (toAddress) to which the application would send mail.

We then simulate the process of composing and clicking on the send button programmatically, by using an SMTP library which has methods that do the equivalent – generating a mail packet, invoking the our mail server configured and sending the mail packet to the recipient for us.

Sending Mails with ASP.NET Core

.NET has a built-in library called System.Mail which does this, but MailKit offers a whole lot of features and is also easy to configure and use. MailKit depends on another library called MimeKit which provides content handling functionality while composing mails.

Installing the Packages

To get started, we install the MailKit and MimeKit packages from the Nuget via CLI. You can also use Nuget Package Manager for the same.

> dotnet add package MailKit
> dotnet add package MimeKit

Creating a MailService abstraction

Let’s begin by creating an abstraction for the MailingService we’re interesting in implementing. This MailingService contains the functionality to send out the content we wish to push, via MailKit.

namespace MailingNinja.Contracts.Services
{
    public interface IMailingService
    {
        void SendSimple(string to, string subject, string messageContent);
    }
}

The method takes in three simple arguments – the recepients emailAddress, the mail subject and the text content.

Implementing the Service

The implementation of this MailingService is as below:

namespace MailingNinja.Core.Services
{
    public class MailingService : IMailingService
    {
        private readonly MailServerConfig _mailServerConfig;

        public MailingService(MailServerConfig mailServerConfig)
        {
            _mailServerConfig = mailServerConfig;
        }

        public void SendSimple(string to, string subject, string messageContent)
        {
            try
            {
                string fromAddress = _mailServerConfig.FromAddress;
                string serverAddress = _mailServerConfig.ServerAddress;
                string username = _mailServerConfig.Username;
                string password = _mailServerConfig.Password;
                int port = _mailServerConfig.Port;
                bool isSsl = _mailServerConfig.IsUseSsl;

                var message = new MimeMessage();

                // the first arg is a name, but we pass
                // the emailAddress in both the places
                message.From.Add(new MailboxAddress(
                    fromAddress, fromAddress));

                // the first arg is a name, but we pass
                // the emailAddress in both the places
                message.To.Add(new MailboxAddress(to, to));
                
                message.Subject = subject;

                // assign the message content
                // to the message body
                message.Body = new TextPart("html")
                {
                    Text = messageContent
                };

                using (var client = new SmtpClient())
                {
                    // connect
                    client.Timeout = 30000;
                    client.Connect(serverAddress, port, isSsl);
                    Console.WriteLine("Connected");

                    // authenticate
                    client.Authenticate(username, password);
                    Console.WriteLine("Authenticated");

                    // send message
                    client.Send(message);
                    Console.WriteLine("Sent");

                    // disconnect
                    client.Disconnect(true);
                    Console.WriteLine("Disconnected");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

How this Service works

This simple functionality creates a new MimeMessage() instance and adds the necessary information such as the server address of the mail server (ex: mail.google.com), the recipient email address, the port of the smtp server (ex. 25) and so on.

While building the message body, observe that we are declaring the content body as type “HTML”. It indicates that whatever html document we pass will be parsed and rendered over the message body. This facilitates for all the styles we shall apply over the html document we send.

Lastly, we end by placing this MimeMessage() object over an SmtpClient() object created and sent over by calling the Send() method.

Now that this is done, we complete this by adding this MailHelper as a service under the ConfigureServices() method in Startup.cs

The MailServerConfig is a singleton class that wraps the Configuration. This is injected via the IOptions pattern. You can find a detailed explanation of the Options pattern here – Working with Options pattern in ASP.NET Core (.NET 5)

// Startup.cs

services.Configure<MailServerConfig>(
    builder.Configuration.GetSection("MailServerConfig"));

services.AddSingleton<MailServerConfig>(
    x => x.GetRequiredService<IOptions<MailServerConfig>>().Value);

// Mailing Service
services.AddSingleton<IMailingService, MailingService>();
// appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "MailServerConfig": {
    "FromAddress": "******",
    "ServerAddress": "******",
    "Port": 25,
    "IsUseSsl": false,
    "Username": "******",
    "Password": "******"
  }
}

Calling the Service

We’ll call this method whenever we want to send out mails to intended recepients with a required content.

For example, we’ll call this method as below with the content obtained from a rendered Razor View as below:

namespace MailingNinja.Web.Controllers;

public class HomeController : Controller
{
    private readonly IMediator _mediator;
    private readonly ITemplateService _templateService;
    private readonly IMailingService _mailingService;
    
    public HomeController(
        IMediator mediator, 
        ITemplateService templateService, 
        IMailingService mailingService)
    {
        _mediator = mediator;
        _templateService = templateService;
        _mailingService = mailingService;
    }

    public async Task<IActionResult> IndexAsync(
    DateTime tillDate, string doAction = "", string emailAddress = "")
    {
        if (tillDate == DateTime.MinValue)
        {
            tillDate = DateTime.Now;
        }

        var getAllNinjasTillToday = new GetAllForGivenDate(tillDate);
        var data = await _mediator.Send(getAllNinjasTillToday);

        if (doAction == "mail")
        {
            await SendReportMailAsync(data, emailAddress);
        }

        return View(data);
    }

    private async Task SendReportMailAsync(
        IEnumerable<NinjaDTO> data, string emailAddress)
    {
        var content = await GetGridContentAsync(data);
        _mailingService.SendSimple(emailAddress, "Your Report", content);
    }

    private async Task<string> GetGridContentAsync(IEnumerable<NinjaDTO> data)
    {
        var templatePath = "MailingTemplates/List";
        return await _templateService.GetTemplateHtmlAsStringAsync(
            templatePath, data.ToList());
    }
}

Buy Me A Coffee

Found this article helpful? Please consider supporting!

Conclusion and… boilerplate 🥳

In this way we can send a html document as body to a mail. We can advance this further by adding a Header (or a Footer) Image and adding an attachement to this Mail we’re sending. You can find this advanced implementation here.

The code snippets used in this demonstration are a part of the boilerplate solution MailingNinja, which perfectly implements concepts for sending mails with ASP.NET Core (.NET 6) and MailKit.

The solution demonstrates:

  • RazorView rendering and extract HTML
  • Send out Mails with Images and Attachments
  • Generate PDF reports from HTML

You can find the complete solution here – MailingNinja Boilerplate Repository

Please do leave a star ⭐ if you find it helpful.


Buy Me A Coffee

Found this article helpful? Please consider supporting!

Ram
Ram

I'm a full-stack developer and a software enthusiast who likes to play around with cloud and tech stack out of curiosity. You can connect with me on Medium, Twitter or LinkedIn.

Leave a Reply

Your email address will not be published. Required fields are marked *