How to send emails from C#/.NET - The definitive tutorial

TOC

I just answered some questions from a friend of mine about a subject that I believe would fit within my how-to-series of posts. That's where I try to come up with updated answers to common C# challenges like converting between formats or saving content to a file. For today's post, I'll show you different ways of sending emails from C#/.NET.

Sending out emails isn't what most developers consider a fun task. Emails have existed like always and every (well almost) system needs to send out one or more emails to the users. In my experience, creating a good solution for sending out emails, can be quite fun and a challenging task if every aspect is to be taken into account. Like how to handles bounces. In this post, I'll show you a range of options for sending out emails and some of the worst practices to avoid.

Using SMTP

.NET and .NET Core comes with built-in support for sending emails through the System.Net.Mail namespace. While it might seem like the easy choice, you will need an SMTP server for this to work. I hosted my own SMTP server back in the days and that isn't something I would recommend anyone doing today.

To test sending emails through SMTP, you can use your Gmail account or sign up for a new one. To use Google's SMTP servers, you will need to enable Less secure app access on your profile's Security page:

Like the message says, allowing less secure apps aren't recommended. So, while it serves our test purpose, you should consider using something else when running in production. All major email providers offer sending out emails based on SMTP.

If the Less secure app access isn't available on your profile, it might be because two-factor authentication is enabled.

The only thing left is a bit of code:

var smtpClient = new SmtpClient("smtp.gmail.com")
{
    Port = 587,
    Credentials = new NetworkCredential("email", "password"),
    EnableSsl = true,
};
    
smtpClient.Send("email", "recipient", "subject", "body");

In the example, I'm creating a new SmtpClient which is the key class when needing to communicate with Google's SMTP servers. The class accepts the hostname (`smtp.gmail.com`) in the constructor. Google expects you to use port 587 through a secure connection that is set up using the Port, Credentials, and EnableSsl properties. You need to replace email with your full email address and password with your Google account password. In the last line, I call the Send-method. Again, replace email with your full email address and recipient with the full email address of the person who should receive the email.

Monitor errors when sending emails

➡️ Reduce errors by 90% with elmah.io centralized error logging and uptime monitoring ⬅️

There's an overloaded Send-method that accepts a MailMessage object to help build the email message:

var mailMessage = new MailMessage
{
    From = new MailAddress("email"),
    Subject = "subject",
    Body = "<h1>Hello</h1>",
    IsBodyHtml = true,
};
mailMessage.To.Add("recipient");

smtpClient.Send(mailMessage);

By using the MailMessage class, you have access to several new properties, like the IsBodyHtml used to include HTML inside the Body property.

Attachments

Attaching files to an email is easy. It requires you to use the MailMessage class as seen in the previous example:

var mailMessage = new MailMessage
{
    ...
};

var attachment = new Attachment("profile.jpg", MediaTypeNames.Image.Jpeg);
mailMessage.Attachments.Add(attachment);

The code expects a file named profile.jpg to exist in program directory. A full path can be used as input as well, and even more overloads accepting Stream exists.

SMTP settings in config

Until now, I have added configuration directly in the C# code. For obvious reasons, you don't want that in a real-life scenario. Also, you may want to switch settings, depending on which environment the code is currently running in.

In .NET, SMTP settings has its own config section (beneath the system.net element) in the app.config and web.config file:

<mailSettings>
    <smtp deliveryMethod="Network">
      <network host="smtp.gmail.com" port="587" userName="email" password="password" enableSsl="true" />
    </smtp>
</mailSettings>

Once settings is present in the config file, you simply create a new instance of the SmtpClient using the empty constructor:

var smtpClient = new SmtpClient();

.NET Core doesn't have a similar concept and totally replaced the configuration system. Declaring SMTP settings in appsettings.json can be easily done:

{
  "Smtp": {
    "Host": "smtp.gmail.com",
    "Port": 587,
    "Username": "email",
    "Password": "password"
  }
}

Then referenced from code like this:

var builder = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json");
var config = builder.Build();

var smtpClient = new SmtpClient(config["Smtp:Host"])
{
    Port = int.Parse(config["Smtp:Port"]),
    Credentials = new NetworkCredential(config["Smtp:Username"], config["Smtp:Password"]),
    EnableSsl = true,
};

.NET configuration also provides strongly typed options through the IOptions interface. There are thousands of blog posts available showing how to do this, why I don't want to repeat it here. Check out AppSettings in ASP.NET Core for details on how to make strongly typed config classes in ASP.NET Core.

MailKit

I have some bad news for you. While the SMTP client available in .NET and .NET Core works just fine, Microsoft recommends not to use it:

SmtpClient always covered my needs, but let's look at the alternative Microsoft recommends: MailKit. MailKit is an open-source .NET library supporting IMAP, POP3, SMTP, and much more. It has an interface very similar to the built-in SMTP client. Let's rewrite the simple mail example from SmtpClient to MailKit. Start by installing the MailKit NuGet package:

Install-Package MailKit

Sending an email looks very familiar with MailKit:

var mailMessage = new MimeMessage();
mailMessage.From.Add(new MailboxAddress("from name", "from email"));
mailMessage.To.Add(new MailboxAddress("to name", "to email"));
mailMessage.Subject = "subject";
mailMessage.Body = new TextPart("plain")
{
    Text = "Hello"
};

using (var smtpClient = new SmtpClient())
{
    smtpClient.Connect("smtp.gmail.com", 587, true);
    smtpClient.Authenticate("user", "password");
    smtpClient.Send(mailMessage);
    smtpClient.Disconnect(true);
}

Like already mentioned, MailKit supports a range of other scenarios like reading emails through IMAP. If you are looking for something more advanced than sending a simple message, MailKit is a good choice. In the following sections, I will present you for a range of cloud-based alternatives to SmtpClient and MailKit.

Using AWS Simple Email Service

Like already mentioned, hosting your own SMTP server isn't ideal. In addition to that, building the content of the email to look good in all email clients, can require quite a lot of code. We switched our email generation needs to an external provider years ago and switched the product on the way. We currently use Simple Email Service (SES) from Amazon Web Services (AWS).

Don't let the name fool you. SES is indeed simple to use but it offers a wide range of features like email templates with handlebars code, bounce handling, send statistics, and much more.

A quick note about SES and SMTP. When signed up, you will notice the SMTP Settings menu item inside AWS. SES can be used simply as an SMTP server, meaning that all of the code from the SMTP section in this post, apply for SES too. In this section, I will show you some of the more advanced features of SES, to avoid making it a copy of what we already discussed, only with SES config.

To test SES, let's upload a template to AWS. A template is a JSON file containing an email template. The nice thing about using templates is that you only need to tell SES which email to send and provide it with any dynamic variables. The email looks like this:

<h1>Hello {{firstname}}</h1>
<p>Welcome as a new user on our amazing service.</p>

SES email templates cannot be uploaded through the AWS Console, why you need to embed the content in a JSON file:

{
  "Template": {
    "TemplateName": "my-email-template",
    "SubjectPart": "Welcome",
    "HtmlPart": "<h1>Hello {{firstname}}</h1>
<p>Welcome as a new user on our amazing service.</p>"
  }
}

The JSON contains a name (slug), a subject and the HTML from the example. To upload the template, you can use the AWS CLI:

aws ses create-template --cli-input-json fileb://my-email-template.json

Sending the email is easy using the excelent SES package for .NET:

Install-Package AWSSDK.SimpleEmail

And a bit of C# code:

var emailClient = new AmazonSimpleEmailServiceClient(
    new BasicAWSCredentials("accessKey", "secretKey"),
    RegionEndpoint.USWest2);

var data = @"{
    ""firstname"": ""Ragnar""
}";

var sendRequest = new SendTemplatedEmailRequest
{
    Source = "Sender <email>",
    Destination = new Destination { ToAddresses = new List<string> { recipient } },
    Template = "my-email-template",
    TemplateData = data,
};

await emailClient.SendTemplatedEmailAsync(sendRequest);

To send emails, we need a new instance of the AmazonSimpleEmailServiceClient class. The class needs your access and secret key, available on the AWS Console. You also provide the region hosting your SES.

The SendTemplatedEmailRequest corresponds to the MailMessage class from the previous examples. It tells AWS who sends the email, who should receive it, as well as the template name and input data.

Finally, the SendTemplatedEmailAsync-method sends the email through SES.

Using Mandrill

There are a lot of email solutions out there. The one we used before switching to AWS, was Mandrill (by Mailchimp). Mandrill offers a lot of the same features as AWS SES, but the cost is higher. With the higher cost, you also receive some additional features not available on AWS. Like an online template builder, that I won't go into details with here. It works pretty much like templates on SES and handlebars codes can be embedded in the template. We simply moved our Mandrill email templates to SES and everything worked out of the box.

There are a couple of different .NET clients available, but the best one (IMO) is Mandrill.NET:

Install-Package Mandrill.net

The usage corresponds to that of SES:

var mandrillApi = new MandrillApi("apikey");

var mandrillMessage = new MandrillMessage
{
    FromEmail = "email",
};
mandrillMessage.AddTo("recipient");
message.AddGlobalMergeVars("firstname", "Floki");

await mandrillApi.Messages.SendTemplateAsync(mandrillMessage, "my-email-template");

For more information about sending emails through Mandrill, check out Sending transactional emails using Mandrill and .NET.

Using SendGrid

Yet another mail provider with good .NET support is SendGrid. SendGrid supports both SMTP and a .NET client. With SMTP, you can use either the SmtpClient class or the MailKit package that I showed you already. All you need to do is to replace the SMTP server, port, and credentials with those obtained from SendGrid.

Since we already covered how to send mails using SMTP, let's take a look at SendGrid's .NET client. Start by installing the SendGrid NuGet package:

Install-Package SendGrid

Sending emails require pretty much the same pattern that we've already seen multiple times:

var sendGridClient = new SendGridClient("API_KEY");
var from = new EmailAddress("from email", "from user");
var subject = "subject";
var to = new EmailAddress("to email", "to name");
var plainContent = "Hello";
var htmlContent = "<h1>Hello</h1>";
var mailMessage = MailHelper.CreateSingleEmail(from, to, subject, plainContent, htmlContent);
await sendGridClient.SendEmailAsync(mailMessage);

What about Azure?

It comes to a chock for many (including me) that Azure doesn't provide an email solution. There is a range of integration with external services like SendGrid and Mailjet, but there's no native service available.

Using an external service like SES, Mandrill and SendGrid works great from Azure, but notice that you pay for outgoing traffic from Azure. This means that you will be charged for the traffic coming from within your Azure data center and to the external mail provider. Using templates is a great way to reduce the bytes sent back and forth.

Among the different solutions I have mentioned in this post, SendGrid is the easiest solution to get started with from Azure. Creating a new SendGrid account is available directly in the Azure Portal by clicking the Create a resource button and searching for "SendGrid":

Features steps
We monitor your websites

We monitor your websites

We monitor your websites for crashes and availability. This helps you get an overview of the quality of your applications and to spot trends in your releases.

We notify you

We notify you

We notify you when errors starts happening using Slack, Microsoft Teams, mail or other forms of communication to help you react to errors before your users do.

We help you fix bugs

We help you fix bugs

We help you fix bugs quickly by combining error diagnostic information with innovative quick fixes and answers from Stack Overflow and social media.

See how we can help you monitor your website for crashes Monitor your website