Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / PowerShell

Universal SMTP Code to Send Emails in .NET Apps

,
Rate me:
Please Sign up or sign in to vote.
5.00/5 (13 votes)
8 Aug 2017CPOL18 min read 37.9K   1.1K   34   11
Since I wrote an article previously, I had been asked on various occasions to share the code on Yahoo! or Bing, etc. I wanted to write an article, with the code, which covers all of those vendors as well.

Introduction

As already mentioned quite clearly in the tagline, the only reason to write this article is to cover the basic requirements of every SMTP vendor's code in .NET applications, If your vendor is Google's mail service, — Gmail — then you can most likely leave this article and go back to the article, which I wrote quite a lot of time ago to share how to send emails through a C# program, Sending emails over .NET framework, and general problems – using C# code. That article will cover the most important parts of understanding the libraries, and how you should communicate with all the settings, etc., to be able to send emails from your own account.

Thus, the benefit I would conclude from this article would be that I can easily share this article with anyone who might want to ask questions such as:

Quote:

How to send an email using {your SMTP provider}?

Or to the questions, where the OP is asking us to review their code for any mistakes. They need to understand that problems with sending emails are not due to bad coding only, they involve several other problems as well. In this post, all those points would be covered, so that there can be a standard "working" SMTP code, which you can try out in your own application to determine whether the problem is in the code, or the account which is being used to send the email.

This article, content provided and the code sample is intended to be provided as a reference only, and might contains bugs in it. It is up to you, to decide to either copy/paste shamelessly and try it in your code, or just get the useful stuff out of this post and consider it for your applications.

Design of the Code Provided Here

Before I go down and start working out the stuff that I have implemented and where I found that, I want to tell you about the design of this code, how I did it and how it might be useful for you. I will be trying to solve out a few questions, which I why the design of the code required me to write a factory class at the background to provide me with the objects of different SMTP vendors, in this code sample, I have implemented different objects for:

  1. Google Mail
  2. Yahoo Mail
  3. Hotmail — the Outlook as we know it
  4. Office 365
  5. SendGrid — I found quite a lot of questions, circulating around SendGrid API so I wanted to share the code, which does indeed send the email from their servers.
  6. Custom object, to implement anything in a different manner.

These are the major SMTP service providers, available out there and mostly questions come for their users. The code sample as provided on GitHub, would help you to either understand how to write the code for sending emails, or would help you in understanding how to write the SmtpClient objects, for each one of them. Sometimes, they require a different configuration and sometimes they are all just same robots, with different names.

That said, there were a few things contradicting the present situation of SMTP providers and the way accounts are managed, which would require me to add a different section at the bottom of this article to support and talk about all those aspects of the user accounts to enable SMTP programming against an account, through a .NET application.

The ISmtpClient Interface

I started with creating an interface, which would contain the information required to create a concrete implementation of an SMTP client. The purpose of this interface was to make sure that I can implement this interface in multiple classes, and provide the implementation for them. There were however a few problems — which I would discuss at the end of this article. A simple introduction to the interface, would seem a bit helpful, right, so...

  1. Interface was a definition of an SMTP client, which would hold.
    1. SMTP service host address
    2. Connection port information
    3. SSL enabled or not
    4. Message to send
    5. Etc.
  2. SMTP host could be changed later one.
  3. Port address, which can be default (25) or configured (587, or anything else, which a server expects).
  4. SSL support enabling, though it was always required.
  5. The credentials to use while communicating with the server.

Thus, I ended up with the following interface:

Image 1

Figure 1: ISmtpClient interface diagram.

I am going to skip a few things from here, so that I can explain them in a later section below (such as, the IDisposable implementation in ISmtpClient), so please bear with me. But, so far we had the basic implementation which can be utilized in the further processes, to create the SMTP clients — just asking, am not I repeating this sentence just too many times? :D

Alright, now the next section talks about how I implemented this ISmtpClient interface. Keep reading.

Creating the factory class for SmtpClient instances — SmtpClientFactory

Once, this interface was developed, I went on to create a factory class, which would help me in creating different SMTP clients for different services. I am not a fan of design patterns, as I never understand any of them... But somehow, I always happen to implement them in my programs and my coauthor told me, that what-ever I had come up with in the program was actually the Factory Pattern, oh right beggars can't be choosy I guess. To design the core creator of the clients, I created an enum to specify the type of clients I would be creating and using further down the stream, the enum was as simple as:

C#
public enum ClientType { Gmail, Outlook, Yahoo, Office365, SendGrid, Custom }

As you can see, this enumeration has a value for each possible client that I am interested in. Plus, a special kind of client which does not share anything to the eye of the reader, but just an idea that this is a custom client and must be provided with everything on the runtime, as the factory pattern would be expecting a lot of information for this fellow.

The implementation of ISmtpClient required me to pass a few of the most important elements to the factory class' main method, which I did and (although skipping now), the class instances of the respective types were generated. Following the factory pattern, the creation of objects was returned as the instance of interface, by creating an instance of the implementing class, not the interface itself.

Image 2

Figure 2: SmtpClientFactory class diagram, with the supported client types enumeration.

The enum part of this class has already been discussed, which leaves me one function to talk about. The GetClient function is the core function, which takes care of the abstraction and plays the role of creating the instances and provided us with them. Now, the function itself was implemented like the following:

C#
// Method returns the ISmtpClient as an instance of the required object
public static ISmtpClient GetClient(ClientType type, NetworkCredential credentials, 
              bool withSsl, string host = "", int port = 25)
{
    ISmtpClient client;

    if (type == ClientType.Gmail)
    {
        client = new GmailClient(credentials, withSsl);
    }
    else if (type == ClientType.Office365)
    {
        client = new Office365Client(credentials, withSsl);
    }
    else if (type == ClientType.Outlook)
    {
        client = new OutlookClient(credentials, withSsl);
    }
    else if (type == ClientType.Yahoo)
    {
        client = new YahooClient(credentials, withSsl);
    }
    else if (type == ClientType.SendGrid)
    {
        client = new SendGridClient(credentials, withSsl);
    }
    else
    {
        // Custom
        if (host == "")
        {
            throw new Exception("Host name is required for a custom SMTP client.");
        }
        client = new CustomClient(host, port, withSsl, credentials);
    }
    return client;
}

For the time being, ignore the classes which I will discuss in the next section. Until then, just understand the fact that this function returns the client which we require. It expects the users, to input:

  1. ClientType to decide what sort of client is being expected here. Remember that the rest of the stuff depends on the type of the client, credentials, SSL and no SSL, port and other settings all depend on this first parameter.
  2. NetworkCredential object, to store the credential for the user, such as his/her username and password combination.
  3. SSL allowance, however in my own experience I have figured that you are always required to use SSL in the major SMTP vendors such as, Google Mail, Outlook, etc.
  4. Host and port settings are required to create the CustomClient object, which requires a bit more control over how the communication is done, such as setting SSL off, or using a different port. However, a default port of 25 is used.

There is nothing special in this code to talk about, other than the fact that it checks if the host name is empty, and the client is of type CustomClient, then it prompts the users to enter something and throws that exception.

Only a Few of the Implementations

I do not want to share a whole lot of code of the same redundant implementations, instead I just want to share a few of them here for you so that you can get the gist of what I am trying to convey here. Rest assured, you can always read the code sample on GitHub, which is made available to you — see at the top in downloads section.

C#
// Implementing the interface, to capture the fields and functions.
public class GmailClient : ISmtpClient
{
    // Host acts as a getter-only property here, implemented via lambda.
    public string Host { get => "smtp.gmail.com"; }
    public int Port { get; set; }
    public bool EnforceSsl { get; set; }
    public NetworkCredential Credentials { get; set; }
    public SmtpClient Client { get; set; }

    public GmailClient(NetworkCredential credentials, bool ssl)
    {
        Credentials = credentials;
        Port = 25;
        EnforceSsl = ssl;
        if (ssl)
        {
            Port = 587;
        }

        Client = new SmtpClient(Host, Port);
    }

    // Async function to send the email, whereas the event handler can still be applied. 
    async Task ISmtpClient.Send(MailMessage message)
    {
        Client.EnableSsl = EnforceSsl;
        Client.Credentials = Credentials;

        // Send
        await Client.SendMailAsync(message);
    }

    // The Dispose() function, to properly send the QUIT command to SMTP server
    public void Dispose()
    {
        Client.Dispose();
    }
}

Rest of the clients which are provided with a named type all implement the service in a similar manner, whereas the custom client type is a bit different. So, that is why the other type of the client which I am going to show you how to implement, is the custom client type.

C#
// Implementing the interface, to capture the fields and functions.
public class CustomClient : ISmtpClient
{
    // Needs a setter, so that object can set Host information later on
    public string Host { get; set; }
    public int Port { get; set; }
    public bool EnforceSsl { get; set; }
    public NetworkCredential Credentials { get; set; }
    public SmtpClient Client { get; set; }

    // Constructor, requires somewhat more information.
    public CustomClient(string host, int port, bool ssl, NetworkCredential credentials)
    {
        // In the custom mode, we can set the host ourself.
        Host = host;
        Port = port;
        Credentials = credentials;
        EnforceSsl = ssl;

        Client = new SmtpClient(Host, Port);
    }

    // Async function to send the email, whereas the event handler can still be applied. 
    async Task ISmtpClient.Send(MailMessage message)
    {
        Client.EnableSsl = EnforceSsl;
        Client.Credentials = Credentials;

        // Send
        await Client.SendMailAsync(message);
    }

    // The Dispose() function, to properly send the QUIT command to SMTP server
    public void Dispose()
    {
        Client.Dispose();
    }
}

The only major difference in the both implementations is their constructor, because one of them requires somewhat more information as compared to the other one, which already contains information about the host and a few other stuffs.

Secondly, like we saw previously in the SmtpClientFactory class, we were using the extra parameters to fill in the details of the custom client,

C#
GetClient(ClientType type, NetworkCredential credentials, bool withSsl, string host = "", int port = 25)

Corresponds, to the following constructor:

C#
public CustomClient(string host, int port, bool ssl, NetworkCredential credentials)

Whereas, the other ones, don't get the values and our program does not require to pass those values at all. In such cases, it would be useful to always follow the type, and ignore passing hardcoded values. However, there might be cases where we need to pass in some of the information directly, such as the cases where we do not know of the host names, port to connect at and whether SSL is required or not. Of course, there are services out there, some insecure services, which provide free access to their servers for sending emails and in turn they track your emails, content and personal information to sell and get their bargain.

Implementing the IDisposable Interface

As mentioned above, it is time to talk about the implementation of IDisposable interface in my own ISmtpClient interface, where it was already implemented by the SmtpClient of .NET framework. The thing is, when I was writing the application code, I was not implementing the interface in my own interface, instead I was using something like this:

C#
// I know the signature says, "async Task ISmtpClient.Send(params) {}" but, read the paragraphs below
void ISmtpClient.Send(MailMessage message, NetworkCredential credentials, bool withSsl) {
    // Create the object here
    using (var client = new SmtpClient(Host, Port)) {
        /* Other stuff, 
         * Send the email here, do a proper clean out, etc.
         */ 
    }
}

Then, later on, I was to figure out that there is another API in SmtpClient as well, which allows you to send the emails asynchronously and it is always recommended to consider the async approaches, as compared to the synchronous ones, unless you really know what you are doing and it is required to grab the synchronous code. Now, with this code, I would require to actually wait out inside the function or at least have the object wait until the email is sent out or so, before cleaning out the object.

The reason to bring the IDisposable out here, was so that I can bring the control of disposing the objects properly out to the scope of my program itself, and control when the object would be disposed off. Thus, to make the required changes, I did the following:

  1. Changed the function signature to mark is as an asynchronous context
  2. Changed the return type from void, to Task — which in turn is a void in the asynchronous context.
  3. Applied the await keyword and used the asynchronous function.

The "using" context is helpful where you have to clean up the resources, which a program has been consuming for a while. You can read more about this in one of my previous articles, and you can also look for it on the MSDN or Microsoft Docs website as well. Pretty much, it is a language construct, which allows you to ensure that an object releases all the resources it is holding up, before going out of scope. I did not want to miss this important factor out of the code before writing it down, that is why it was important to make sure somewhere a using block is being used by the code. As you will see onwards, the Dispose function of SmtpClient is called to send a QUIT command to the server and properly terminate the connection before leaving the context and being deleted by the GC.

The explanation of the async context is in the next section, please continue reading.

Asynchronous Functionality in the Clients

Now that we have bits and bytes merged and summed up, let us apply a few final polishing marks to the clients and make it asynchronous for the graphical applications. The previous function, was changed to the following code sample now,

C#
// Async keyword, and the Task return type
async Task ISmtpClient.Send(MailMessage message)
{
    Client.EnableSsl = EnforceSsl;
    Client.Credentials = Credentials;

    // Sending of the mail message asynchronously, and waiting till this finishes. 
    await Client.SendMailAsync(message);
}

The benefit of the two changes, first being the IDisposable, and the second one being the addition of async/await keywords to the content, allow me to write an asynchronous code as well as maintaining the disposing off the SmtpClient object properly.

Better heads up, because there is another function SendAsync(). That function does not work with async/await design, because the function returns a void and thus cannot be awaited. That is why, if you are considering to use async/await, then you must avoid that function and instead consider the one with "Mail" in it. Now majority of our code has been taken care of, let us move down a step and try sending the emails from our code.

Note: I will not send emails by every client, as I already have done that, instead I will only send email through the default Google Mail client and show you what it does, and a few suggestions by the end of this article.

Getting Started with System.Net.Mail

The first thing you would want to do is to understand what sort of support does .NET framework provides for email delivery. There are several things that you can handle in the SmtpClient object, and that is what we want to see in this section now. The SmtpClient object, in .NET Framework supports the basic level of SMTP communication, as it is defined in the RFC 2821. You need to look over that resource to learn more about the SMTP. What I am going to talk about here, in this post, is a few of the best practices to ensure that your application keeps running, and doesn't crash obviously.

Optimistic Try — Successful Hit

Now, first I will try the working code and show you what I have, there is no need to share the code again, or say which client I used. The thing to notice here is, that the clients work if you provide them the accurate details, and I will discuss those accurate details in a minute. Here is the result of my trial:

Image 3

Figure 3: "Email sent successfully." message being shown in a terminal.

This proves the fact that this code works. Secondly, I tested the code with the best practices provided by each individual service provider, to ensure the general code is the exact skeleton of what is required here. Now, let us try to break the code once. The email message was:

Image 4

Figure 4: The email message being shown in the webmail client.

Moving onwards, now.

Breaking the Code in Several Ways

This is the part, where the .NET framework's SmtpClient annoys you. The API doesn't tell you whether the connection is setup, working, ready to accept the email messages, or not. The only point where it would tell you is when you will call the Send function! Which is just not at all useful, but if we have to stick to everything-.NET, then there is nothing we can do. In this client, there are several things that can go wrong. Let me list a few out to you.

  1. Your credentials are faulty
  2. Your port was incorrect, perhaps you wanted to use the default port (25) instead of the secure one.
  3. You didn't enable the SSL based communication, and wanted to communicate over SSL channel.

In all these cases, you just need to ensure that the SSL channel is enabled before you send the email.

C#
// somewhere inside the client

client.EnableSsl = true;

// rest of the stuff

Almost, in every possible way, the client would break and give you this error — or a similar one.

Image 5

Figure 5: Exception message being shown in the Visual Studio IDE.

The exception was captured in the Visual Studio's window, but you get the idea. The error is thrown in most cases, and this is most-likely-encountered error in SMTP programming with .NET, because the API does not show any interest in letting you control further states of the server-client communication here. However, the primary reason of this error was that I set the EnableSsl field to false.

Bonus: Understanding Behavior of Different Providers

Finally, I would like to give a bit of talk on the way different providers are implemented and how they expect their customers to access the APIs. One thing we know is that all these vendors are service-providers, for consumers and are primarily used as consumer-based and not as enterprise-oriented (SendGrid, Office 365 and Google G Suite, etc. are an exception, and other SMTP providers also, generally speaking). They all have a similar behavior, and provide a similar service. On my conquest for this article, otherwise, there might still be a question like:

Quote:

I have enabled SSL, and my username/password is correct, but I am still unable to send an email. What might be wrong?

Such questions require a bit of provider-specific details and require to dig a bit deeper in each one of them. I noticed a few things in all of them and here in this final section, I would like to share them with you...

Security Implementations

Since, all these vendors are consume-based, they disable programmability on their services. Google Mail, and Yahoo, for instance required me to go in the settings and enable not only the SMTP services, but low-security applications as well.

For example, have a look at the following screenshot of my own inbox, where Google Mail is telling me that I need to check my account details (e.g., password) being compromised, or I should allow those device to access if I know what is going on.

Image 6

Figure 6: Google Mail showing a sign-in attempt warning to a user, notifying them of unusual activity.

There are several other checks, that are implemented to ensure that your account is safe. Whereas on the other end, in the application service providers such as Office 365, or SendGrid, your account will be secured with strong credentials as they are enterprise-oriented service providers and they don't like to lock out their users once they have all logged in with the API keys or username/password combinations.

The Communication-Security

Now let us talk about the security of the communication, what happens when the mail travels on the wire? This is one of the most confusing thing in the entire API. The API says, that the communication is done on SSL, which is utter bullshit... The communication does not take place on SSL, instead it takes place on TLS (which is not SSL).

Thus, when you read the following line of code:

C#
client.EnableSsl = true;

Please, understand the fact that this is TLS going on, and not the SSL. I would recommend that you read a bit more about TLS (Transport Layer Security) and the SSL (Secure Socket Layer), to get a better understanding of them both.

Please go through the following links at RFC documents, to get a better understanding of how things go:

  1. The TLS Protocol Version 1.0
  2. Simple Mail Transfer Protocol
  3. SMTP Service Extension for Secure SMTP over Transport Layer Security

By the end of reading those links, most of the confusions would be removed as the confusions are merely due to the naming conventions and the people telling out names of these technologies. Nothing else.

Secondly, not sure, but all the clients require you to enable SSL communication, otherwise they will always cause the error. It is also a better practice to enable SSL and then send email, you should avoid using the servers and service providers where security is not taken seriously. Even if nothing serious happens, your email account would be left open to the public and spammers are always looking for email addresses.

Miscellaneous

Or as I call it, the "Final words". All of the SMTP providers are following the RFC documents, which I have shared with you above, and it is always recommended to find a library which conforms to those standards, and I have found that .NET framework's SmtpClient has a poor implementation, the reasons for which are obvious and shared in the article (e.g., the client is unable to tell whether connection is established or not, or what is the status of the connection, unless you actually try to do something). I am also trying to get a useful library and would update once that is made available, until then there is very less to say.

In my quest, I found that the Office 365 were a bit sluggish, to send the emails and generate the responses, and the SendGrid was even worse. What SendGrid did was they responded back with an OKAY (or whatever SMTP alternative for this English command is), but they didn't send an email out till a while... which was disturbing for me and doesn't count as a speed-send. Google Mail, Yahoo and Outlook (not the Office 365) were all quite fast enough for me.

Nonetheless, for a while, my default email provider stays the Google Mail and soon I might be migrating to Office 365 or Outlook and thus would be investigation why their servers are a bit late in sending email.

I hope you have found this article useful, and would be using the content provided here as a reference, or for any purpose you might want. The code is not a simple sample function as it was previously, but instead is a complete library-sort-of-thing, so you can take what you need and implement it in your own code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Pakistan Pakistan
Afzaal Ahmad Zeeshan is a computer programmer from Rabwah, Pakistan, currently living in The Netherlands, likes .NET Core and Node.js for regular everyday development. Afzaal Ahmad works at Adyen as a Developer Advocate.

He is an expert with Cloud, Mobile, and API development. Afzaal has experience with the Azure platform and likes to build cross-platform libraries/software with .NET Core. Afzaal is an Alibaba Cloud MVP, twice he has been awarded Microsoft MVP status for his community leadership in software development, four times CodeProject MVP status for technical writing and mentoring, and 4 times C# Corner MVP status in the same field.

Written By
Software Developer StackFinity
United Kingdom United Kingdom
I'm a Test Automation Engineer with a mindset of sharing my knowledge with the community around because helping others drives our success in the long run. My main programming languages are Python, Java, and C#; however, I have written code in many other languages and can pick up new languages and frameworks quickly.

I always keep a goal to reach and currently working on bridging the gap between my thoughts to solve a problem and shaping it into a good code so the computer could get it. I love coffee, night walk, fruits.

Comments and Discussions

 
Questionattachment required Pin
tsantony28-Apr-20 19:05
tsantony28-Apr-20 19:05 
GeneralMy vote of 5 Pin
Richard MacCutchan14-Aug-17 0:45
mveRichard MacCutchan14-Aug-17 0:45 
QuestionMailKit + MimeKit Pin
sps-itsec469-Aug-17 2:45
sps-itsec469-Aug-17 2:45 
AnswerRe: MailKit + MimeKit Pin
Afzaal Ahmad Zeeshan9-Aug-17 2:57
professionalAfzaal Ahmad Zeeshan9-Aug-17 2:57 
QuestionDoes it works in asp.net core Pin
Mou_kol8-Aug-17 23:34
Mou_kol8-Aug-17 23:34 
AnswerRe: Does it works in asp.net core Pin
Afzaal Ahmad Zeeshan9-Aug-17 0:21
professionalAfzaal Ahmad Zeeshan9-Aug-17 0:21 
GeneralRe: Does it works in asp.net core Pin
Mou_kol9-Aug-17 22:33
Mou_kol9-Aug-17 22:33 
AnswerRe: Does it works in asp.net core Pin
Afzaal Ahmad Zeeshan9-Aug-17 22:56
professionalAfzaal Ahmad Zeeshan9-Aug-17 22:56 
QuestionNice. Some small hints. Pin
ydude8-Aug-17 22:34
ydude8-Aug-17 22:34 
AnswerRe: Nice. Some small hints. Pin
Afzaal Ahmad Zeeshan9-Aug-17 0:17
professionalAfzaal Ahmad Zeeshan9-Aug-17 0:17 
Haha, upvoted.

Nice suggestion, will make sure these practices are followed in my upcoming posts. Thank you! Smile | :)
The sh*t I complain about
It's like there ain't a cloud in the sky and it's raining out - Eminem
~! Firewall !~

GeneralMy vote of 5 Pin
Ehsan Sajjad8-Aug-17 3:34
professionalEhsan Sajjad8-Aug-17 3:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.