In previous posts I’ve talked about how you can now use the legacy SMTPClient class inside .NET to send emails. As commentators on this post have pointed out however, this has now been deprecated and the official documentation actually points you towards a very popular email library called “MailKit“. It’s open source, it’s super extensible, and it’s built on .NET Standard meaning that you can use the same code across .NET Full Framework, UWP and .NET Core projects.
Creating An Email Service
It’s always good practice that when you add in a new library, that you build an abstraction on top of it. If we take MailKit as an example, what if MailKit is later superceded by a better emailing library? Will we have to change references all over our code to reference this new library? Or maybe MailKit has to make a breaking change between versions, will we then have to go through our code fixing all the now broken changes?
Another added bonus to creating an abstraction is that it allows us to map out how we want our service to look before we worry about implementation details. We can take a very high level view of sending an email for instance without having to worry about exactly how MailKit works. Because there is a lot of code to get through, I won’t do too much explaining at this point, we will just run through it. Let’s go!
First, let’s go ahead and create an EmailAddress class. This will have only two properties that describe an EmailAddress.
public class EmailAddress { public string Name { get; set; } public string Address { get; set; } }
Now we will need something to describe a simple EmailMessage. There are a tonne of properties on an email, for example attachments, CC, BCC, headers etc but we will break it down to the basics for now. Containing all of this within a class means that we can add extra properties as we need them later on.
public class EmailMessage { public EmailMessage() { ToAddresses = new List<EmailAddress>(); FromAddresses = new List<EmailAddress>(); } public List<EmailAddress> ToAddresses { get; set; } public List<EmailAddress> FromAddresses { get; set; } public string Subject { get; set; } public string Content { get; set; } }
Now we need to setup our email configuration. That’s our SMTP servers, ports, credentials etc. For this we will make a simple settings class to hold all of this. Since we are good programmers we will use an interface too!
public interface IEmailConfiguration { string SmtpServer { get; } int SmtpPort { get; } string SmtpUsername { get; set; } string SmtpPassword { get; set; } string PopServer { get; } int PopPort { get; } string PopUsername { get; } string PopPassword { get; } } public class EmailConfiguration : IEmailConfiguration { public string SmtpServer { get; set; } public int SmtpPort { get; set; } public string SmtpUsername { get; set; } public string SmtpPassword { get; set; } public string PopServer { get; set; } public int PopPort { get; set; } public string PopUsername { get; set; } public string PopPassword { get; set; } }
Now we actually need to load this configuration into our app. In your appsettings.json, you need to add a section at the root for email settings. It should look something like this :
{ "EmailConfiguration": { "SmtpServer": "smtp.myserver.com", "SmtpPort": 465, "SmtpUsername": "smtpusername", "SmtpPassword": "smtppassword", "PopServer": "popserver", "PopPort": 995, "PopUsername": "popusername", "PopPassword" : "poppassword" } ....Other settings here... }
In the ConfigureServices method or your startup.cs, we can now pull out this configuration and load it into our app with a single line. Основными активными соединениями каннабиса являются каннабиноиды, которые содержат семена каннабиса на сегодняшний день их насчитывается более 60, и большинство из которых являются психотропными.
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<IEmailConfiguration>(Configuration.GetSection("EmailConfiguration").Get<EmailConfiguration>()); }
This allows us to inject our configuration class anywhere in our app.
The final piece of the puzzle is a simple email service that can be used to send and receive email. Let’s create an interface and an implementation that’s empty for now. The implementation should accept our settings object as a constructor.
public interface IEmailService { void Send(EmailMessage emailMessage); List<EmailMessage> ReceiveEmail(int maxCount = 10); } public class EmailService : IEmailService { private readonly IEmailConfiguration _emailConfiguration; public EmailService(IEmailConfiguration emailConfiguration) { _emailConfiguration = emailConfiguration; } public List<EmailMessage> ReceiveEmail(int maxCount = 10) { throw new NotImplementedException(); } public void Send(EmailMessage emailMessage) { throw new NotImplementedException(); } }
Head back to our ConfigureServices method of our startup.cs to add in a final line to inject in our EmailService everywhere.
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<IEmailConfiguration>(Configuration.GetSection("EmailConfiguration").Get<EmailConfiguration>()); services.AddTransient<IEmailService, EmailService>(); }
Phew! And we are done. If at this point we decided MailKit isn’t for us, we still have an email service that can swap in and out libraries as it needs to, and our calling application doesn’t need to worry about what’s going on under the hood. That’s the beauty of abstracting a library away!
Getting Started With MailKit
Getting started with MailKit is as easy as installing a Nuget package. Simply run the following from your Package Manager Console :
Install-Package MailKit
And hey presto! You now have access to MailKit in your application
Sending Email via SMTP With MailKit
Let’s head back to our email service class and fill out the “Send” method with the actual code to send an email via MailKit. The code to do this is below :
public void Send(EmailMessage emailMessage) { var message = new MimeMessage(); message.To.AddRange(emailMessage.ToAddresses.Select(x => new MailboxAddress(x.Name, x.Address))); message.From.AddRange(emailMessage.FromAddresses.Select(x => new MailboxAddress(x.Name, x.Address))); message.Subject = emailMessage.Subject; //We will say we are sending HTML. But there are options for plaintext etc. message.Body = new TextPart(TextFormat.Html) { Text = emailMessage.Content }; //Be careful that the SmtpClient class is the one from Mailkit not the framework! using (var emailClient = new SmtpClient()) { //The last parameter here is to use SSL (Which you should!) emailClient.Connect(_emailConfiguration.SmtpServer, _emailConfiguration.SmtpPort, true); //Remove any OAuth functionality as we won't be using it. emailClient.AuthenticationMechanisms.Remove("XOAUTH2"); emailClient.Authenticate(_emailConfiguration.SmtpUsername, _emailConfiguration.SmtpPassword); emailClient.Send(message); emailClient.Disconnect(true); } }
The comments should be pretty self explanatory, but let’s quickly run through it.
- You can send clear text or HTML emails depending on the “TextFormat” you use when creating your message body
- MailKit has named it’s Smtp class “SmtpClient” which is the same as the framework class. Be careful if you are using Resharper and the like that when you click “Add Reference” you are adding the correct reference.
- You should choose to use SSL whenever available when connecting to the SMTP Server
Because we built out our EmailService, EmailMessage and EmailConfiguration classes earlier, they are all ready to be used immediately!
Receiving Email via POP With MailKit
And now the code to receive email via POP.
public List<EmailMessage> ReceiveEmail(int maxCount = 10) { using (var emailClient = new Pop3Client()) { emailClient.Connect(_emailConfiguration.PopServer, _emailConfiguration.PopPort, true); emailClient.AuthenticationMechanisms.Remove("XOAUTH2"); emailClient.Authenticate(_emailConfiguration.PopUsername, _emailConfiguration.PopPassword); List<EmailMessage> emails = new List<EmailMessage>(); for(int i=0; i < emailClient.Count && i < maxCount; i++) { var message = emailClient.GetMessage(i); var emailMessage = new EmailMessage { Content = !string.IsNullOrEmpty(message.HtmlBody) ? message.HtmlBody : message.TextBody, Subject = message.Subject }; emailMessage.ToAddresses.AddRange(message.To.Select(x => (MailboxAddress)x).Select(x => new EmailAddress { Address = x.Address, Name = x.Name })); emailMessage.FromAddresses.AddRange(message.From.Select(x => (MailboxAddress)x).Select(x => new EmailAddress { Address = x.Address, Name = x.Name })); emails.Add(emailMessage); } return emails; } }
Again, all rather straight forward.
While we only retrieve a few basic details about the email message, the actual MailKit email object has a tonne of data you can inspect including headers, CC addresses, etc. Extend as you need to!
I Got Exception XYZ
SMTP can sometimes be a bit tricky getting right in terms of SSL, TLS, and ports. Worse yet, the exception messages are often either cryptic, or start pushing you in the completely wrong direction. I created another article here, that should cover any sort of exception you might get when using this code.
Free SMTP Server
It’s worth mentioning that if you are a hobbyist with your own website and want to just send a few emails every now and again under your own domain. A great solution that I use for this very blog is MailGun. It has a great free plan that for most people will be more than enough, but also paid plans for when you really need to start sending a lot of email.
I like it. I added async versions of the 2 service methods as well. Could be worth adding to this entry.
Can we see async version shomewere on net please ?
Is this a good idea to use this method with me web application’s ‘Contact us form’ ? ‘
Sure, if you wish to send any email via SMTP then this is the library to use!
Ok, thank you 🙂 .
I used your excellent tutorial. It took 5 minutes to copy/paste, but 3 hours to figure out how to send an email by calling the public void Send from within a controller action.
For others not yet aware of how to implement Dependency Injection. It would be good to know that you can inject within an ActionResult parameter like this:
public IActionResult ForgetPW([FromServices] IEmailService Mailer)
or at the controller level like this:
public class ApiController : Controller {
private readonly IEmailService _Mailer;
public ApiController(IEmailService Mailer) {
_Mailer = Mailer;
}
Thanks! This really helps.
Thanks
Thanks for this, but I’m having trouble getting it to work. When I create the below code, I get Inconsistent Accessibility error on the public EmailService(… Any ideas what I’m doing wrong?
public class EmailService : IEmailService
{
private readonly IEmailConfiguration _emailConfiguration;
public EmailService(IEmailConfiguration emailConfiguration)
{
_emailConfiguration = emailConfiguration;
}
Without seeing the rest of the code, Either IEmailConfiguration or IEmailService are not set to be public would be my guess 🙂
Wade, thank you. I didn’t catch that the interface was public!
Hi All, I think I have successfully implemented this now, but I still cannot send emails.
emailClient.Connect(_emailConfiguration.SmtpServer, _emailConfiguration.SmtpPort, true);
The program fails on the above line of code. I have tried it with both false and true for SSL, but it fails regardless.
When set to true, I receive the following error – “IOException: The handshake failed due to an unexpected packet format.”
When set to false, I receive the following error – “AuthenticationException: The remote certificate is invalid according to the validation procedure.”
I have tried it a domain service that I use in another asp.net app (not core) to send emails and I have also tried gmail. Both seem to fail. I have also tried smtp port 25 and the ssl one 465.
Any help is greatly appreciated.
I am trying to implement the same however, i am unable to Can you point me to a walk through that i can see my errors. Need to used this concept for email confirmation and other email within the application
According to you post i am have the same result connecting to an exchange server for both ture and false
False: SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.
The SSL certificate presented by the server is not trusted by the system for one or more of the following reasons:
1. The server is using a self-signed certificate which cannot be verified.
2. The local system is missing a Root or Intermediate certificate needed to verify the server’s certificate.
3. The certificate presented by the server is expired or invalid.
Usually I see this when you are trying to force SSL when the port only supports TLS. Change the “Connect” line to this :
emailClient.Connect([hostname], [port]);
The MailboxAddress .ctor is not meant to parse anything. Use MailboxAddress.Parse() instead.
Hey there,
When you use the constructor, it actually ends up calling Parse anyway. See here
The deprecation was an accidental side-effect of auto-generated docs. The .NET class is no longer marked deprecated in the documentation.
https://github.com/dotnet/docs/issues/1876
It is effectively depreciated and MailKit is the recommendation in the docs.
Hi,
MailKit is completely new to me. Is it possible to use MailKit in VBSCRIPT? I have done some googgling but with no luck. /Mats
New to AspNet Core 2…How to new up EmailService? How do you pull in the required EmailConfiguration if you are storing it in appsettings.json?
Hi Tom,
ASP.net Core has inbuilt dependency injection.
So all you need to do is on your controller, inject the service you need into it (That you registered with the ConfigureServices method). So for example :
public class HomeController : Controller
{
private readonly IEmailService _emailService;
public HomeController(IEmailService emailService)
{
_emailService = emailService;
}
....
}
‘EmailMessage’ does not contain a definition for ‘Content’ and no extension method ‘Content’ accepting a first argument of type ‘EmailMessage’ could be found (are you missing a using directive or an assembly reference?)
Where is content supposed to be defined?
message.Body = new TextPart(TextFormat.Html)
{
Text = emailMessage.Content;
}
is there a GIT repository for source code?
Hi guys, im receiving that error message when I execute de app
An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type ‘MandaEmail40.Models.EmailServico’ while attempting to activate ‘MandaEmail40.Controllers.EnviaEmailController’.
Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
any tip about ?
You’re probably going to have to create a Github gist so I can see what’s going on here. If I had to guess, It’s because you are doing this :
But then in your controller it looks something like :
You need to inject the interface not the class. So :
Hello,
Can you tell me why the dependency injection is not working in asp net core 2.1?
I’m using in a web api, and the following line is not working properly:
private readonly IEmailService _emailService;
Thanks for your attention. I’m looking forward to your reply.
Good article. I’ve got a tip for next one: How to make HTML templates for text/html body using Razor engines.
Hello,
Method ReceiveEmail(int maxCount = 10) I think need to add code:
emails.Add(emailMessage);
into for loop.
However, I want to create singleton service to get notification when get new email, can you help me?
Thanks.
Hi Metro,
Did you manage to create the service?
Kind Regards
I wonder if I still have to use a SSL when my email server and my application, sending the email is hosted in the same internal network. Or should I use port 25 and not force the SSL?
We can pray that someone like Troy Hunt doesn’t see this message… But that sounds OK to me to use port 25 if within the same internal network with no external access. However, I still think using SSL is the wise choice regardless as it’s just another attack vector without it.
hi.
I was going through the documentation for the same, but i am facing problem, while establishing the connection with pop3 mail account.
As soon as i call following statement, i am getting the above error :
emailClient.Connect(“10.123.2.123”,110,false);
No connection could be made because the target machine actively refused it [::ffff:10.123.2.123]:110
Note : server details are changed
I have checked telnet. Server is running and accepting the connection on port 110 from the same machine.
I am also able to login on the server from other mail client applications.
Kindly suggest me what is the error, how to proceed.
try with some differrent port number
Hi, I use this :
but I got this Error message
Can you help me?
Thanks.
The Error message :
ExtendedSocketException: No connection could be made because the target machine actively refused it
Try here : https://dotnetcoretutorials.com/2018/03/18/common-errors-sending-email-mailkit/
Got the source code in GIT?
How do I list received emails?
Nice tutorial! But I think there’s a bug in the ReceiveEmail() implementation. I don’t see where you are adding the emailMessage objects to the emails collection that you are returning.
Just add this to as the list line in the for loop:
emails.Add(emailMessage);
In the Microsoft SmtpClient you had a possibility to set DeliveryMethod=”SpecifiedPickupDirectory” in the app.config. This was very useful when testing. Is there anything similar implemented in MailKit? I’ve looked at the documentation but nothing obvious presents itself
What about attachments? some example? Me in my case i tried following the examples but attachment has 0 bytes.
For anybody else looking at this, Get is now int the package Microsoft.Extensions.Configuration.Binder. This will let you use the
Configuration.GetSection(“EmailConfiguration”).Get()
part without getting a
IConfigurationSection does not contain a definition for ‘Get’
error.
Thanks! It’s nice start point for migration from old code. We used 3rd party libraies for old projects. So I started wtih this during migration to .net core.
Hello,
Would someone please help with the implementation of the OnPost method for the above objects? What do I call and in which order?
Thanks,
Patrick (Newbie.)
Thank you for this tutorial.
Can you please indicate what to reference in order to use IServiceCollection?
Hello, how would you code a solution that retrieves credentials (user, pass) from the network, so passwords aren’t plaintexted anywhere?