Saturday, June 26, 2010

How To Test Sending an Email in .NET?

Sending an email can be tested in many different ways. One option could be setting up an account with an online email provider (Yahoo, Hotmail, Google etc.) and then use that account for sending email.

To save time, however, it might be valuable to look at "SMTP Server for Developers” (SSFD on Codeplex). This simple tool – developed by Antix – gives you a local SMTP server which looks like a standard server from your application’s perspective but on the back side simply writes all emails to a folder. The emails are stored in a text file with the extension EML and with a predefined format (headers + empty line + body).

By using SSFD the round trip will be faster and for retrieving the email you simply read a file.

To configure email in .NET (I am using version 4.0) add the following to your web.config/app.config file (the highlighted line is needed so SmtpClient.Dispose() doesn’t throw an exception, see comments at the end of this post):

<configuration>
  <system.net>
    <mailSettings>
      <!-- Setting for release (need to update account settings): -->
      <!--<smtp deliveryMethod="Network">
            <network host="..." port="25" userName="..." password="..." />
         </smtp>-->
      <!--Setting for development (uses ssfd.codeplex.com):-->
      <smtp deliveryMethod="SpecifiedPickupDirectory">
        <network host="localhost" />
        <specifiedPickupDirectory pickupDirectoryLocation="C:\data\Temp" />
      </smtp>
    </mailSettings>
  </system.net>
</configuration>

Then in your code write the following:

using System;
using System.Net.Mail;
using System.Reflection;

namespace WebPortal.Controllers {
   internal class EmailHelper {
      public static void SendEmail(string from, string to, string subject, string body) {
         try {
            using(var smtpClient = new SmtpClient()) {
               smtpClient.Send(new MailMessage(from, to, subject, body));
            } 
            // SmtpClient.Dispose() may throw exception despite Microsoft's own guide.
            // See blog post for further details.
         }
         catch (Exception ex) {
            Log.Error(ex);
         }
      }

      private static readonly log4net.ILog Log =
         log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
   }
}

Of course this is only the simplest version. If you need to send to multiple recipients or want to use a specific encoding or HTML instead of text format then this code would needs a bit more meat. Furthermore in a production system you may want to add error handling that allows some feedback to the user or the system administrator.

One observation I made while working on this: Microsoft recommends that “a Dispose method should be callable multiple times without throwing an exception”. Unfortunately SmptClient.Dispose() throws an exception when no host has been specified, thus contradicting their own recommendation.

SSFD doesn’t require the hostname for operation but when you implement your client side code you may want to use “using(var smtpClient = new SmtpClient()) {…}” to ensure that all resources used by your code (e.g. server connections) are properly cleaned up. Without a hostname in the web.config file (or specified through some other means, e.g. programmatically) SmtpClient.Dispose() will throw an exception. Therefore even though SSFD doesn’t need it, add “<network host="localhost" />“ as shown in the above web.config example.

No comments:

Post a Comment

All comments, questions and other feedback is much appreciated. Thank you!