Thursday, 28 July 2011

C# - Sending HTML Email template with linked resources and plain text fallback

Hi all,

If your reading this, then chances are you've looked how to create a simple email with C# and added some HTML in there. You might have then looked for including images and the documentation on the internet for this is kinda poor/bad/doesn't work. Well here's how!!

In this example, I have created a class library that can be used with any C# application. So it doesn't matter what kinda application you have (MVC, ASPX Web App, Client App, Command Line etc...).

Lets let on with it!! I've created this project in .NET 4.0 at time of writing and included a VS2010 MVC3 Web App with it for full example. I have also included some global MVC3 error handling!

Code Snippet
  1. // <copyright file="EmailHelper.cs" company="GinkoSolutions.com">
  2. // Copyright (c) 2011 All Right Reserved
  3. // </copyright>
  4. // <author>Sean Greasley</author>
  5. // <email>sean@ginkosolutions.com/sean@tutorialgenius.com</email>
  6. // <summary>Email helper class. Allows sending of html and plain text emails to a target email address.</summary>
  7. namespace MVCEmailExample.Helpers
  8. {
  9.     using System;
  10.     using System.Collections.Generic;
  11.     using System.Collections.Specialized;
  12.     using System.Configuration;
  13.     using System.Linq;
  14.     using System.Net.Mail;
  15.     using System.Net.Mime;
  16.     using System.Web;
  17.     using System.Web.UI;
  18.     using System.Web.UI.WebControls;
  19.     using MVCEmailExample.Exceptions;
  20.     using MVCEmailExample.Models;
  21.  
  22.     /// <summary>
  23.     /// Email helper class.
  24.     /// Allows sending of html and plain text emails to a target email address.
  25.     /// </summary>
  26.     public class EmailHelper
  27.     {
  28.         /// <summary>
  29.         /// Sends an email to a recipient. Provides HTML and plain text views.
  30.         /// The recipient will receive which one their client supports.
  31.         /// </summary>
  32.         /// <param name="templateDir">Directory of where the HTML email template is stored.</param>
  33.         /// <param name="recipient">Receipient information</param>
  34.         public static void SendEmail(string templateDir, Recipient recipient)
  35.         {
  36.             try
  37.             {
  38.                 // Build message
  39.                 MailMessage message = new MailMessage();
  40.                 message.To.Add(new MailAddress(recipient.Email));
  41.                 message.Subject = ConfigurationManager.AppSettings["EmailSubject"];
  42.  
  43.                 // Create plain text mode for alternative view
  44.                 AlternateView plainView = AlternateView.CreateAlternateViewFromString(ConfigurationManager.AppSettings["PlainTextEmail"], null, "text/plain");
  45.                 message.AlternateViews.Add(plainView);
  46.  
  47.                 // Create HTML email version
  48.                 MailDefinition mailDef = new MailDefinition();
  49.                 mailDef.BodyFileName = string.Format(@"{0}\{1}", templateDir, @"Email.html");
  50.                 mailDef.IsBodyHtml = true;
  51.                 mailDef.Subject = ConfigurationManager.AppSettings["EmailSubject"];
  52.  
  53.                 // Build replacement collection to replace fields in Email.htm file
  54.                 // Use fields anywhere in the template file. I.e.   <%FRIENDNAME%>
  55.                 ListDictionary replacements = new ListDictionary();
  56.                 replacements.Add("<%NAME%>", recipient.Name);
  57.  
  58.                 // Use dummy control as owner (I.e. new System.Web.UI.Control()) as were in a class library.
  59.                 // It's only use to determine where the access templates from as a relative base.
  60.                 MailMessage msgHtml = mailDef.CreateMailMessage(recipient.Email, replacements, new System.Web.UI.Control());
  61.  
  62.                 AlternateView htmlView = AlternateView.CreateAlternateViewFromString(msgHtml.Body, null, "text/html");
  63.  
  64.                 // Add linked resources
  65.                 AddLinkedResources(templateDir, ref htmlView);
  66.                
  67.                 // Add HTML view
  68.                 message.AlternateViews.Add(htmlView);
  69.  
  70.                 // Send message
  71.                 SmtpClient client = new SmtpClient();
  72.                 client.Send(message);
  73.             }
  74.             catch (Exception mailEx) { throw new MailerException("Error sending email.", mailEx); }
  75.         }
  76.  
  77.         /// <summary>
  78.         /// Adds linked resources to the email
  79.         /// Email template must contain the resource IDs in the following format: <img src="cid:CONTENTID" />
  80.         /// </summary>
  81.         /// <param name="templateDir">Directory of where the HTML email template images are stored.</param>
  82.         /// <param name="htmlView">A reference to the HTML view.</param>
  83.         private static void AddLinkedResources(string templateDir, ref AlternateView htmlView)
  84.         {
  85.             LinkedResource logo1 = new LinkedResource(string.Format(@"{0}\{1}", templateDir, @"Images\email.jpg"), MediaTypeNames.Image.Jpeg);
  86.             logo1.ContentId = "email";
  87.             htmlView.LinkedResources.Add(logo1);
  88.         }
  89.     }
  90. }
  91.  
End of Code Snippet


Note: This will attempt to send a HTML email with an embedded image (linked resource). If the client does not support HTML, then a plain text email will be used as backup. boom! (I've defined this in the application settings, as well as the email subject and "from" email address).


Configuration - App Settings (Optional, just didn't want to hard code them) [Web/App.config]
Code Snippet
  1. <appSettings>
  2.     <!-- Email App Settings -->
  3.     <add key="EmailSubject" value="GinkoSolutions.com Email Example"/>
  4.     <add key="PlainTextEmail" value="Hi There, You currently don't support HTML emails, but thats ok! I'm just saying hello anyway!"/>
  5. </appSettings>
End of Code Snippet


Configuration - Email server [App/Web.config]
Code Snippet
  1. <system.net>
  2.     <mailSettings>
  3.         <smtp from="Admin &lt;admin@ginkosolutions.com&gt;">
  4.             <network host="localhost" port="25"  />
  5.         </smtp>
  6.     </mailSettings>
  7. </system.net>
End of Code Snippet


Note: This won't work unless you have a local email server. Please provide the details to an SMTP email server here.


Create a sample recipient class (Just for storing recipient details)
Code Snippet
  1. public class Recipient
  2. {
  3.     public string Name { get; set; }
  4.     public string Email { get; set; }
  5. }
End of Code Snippet


Invoke the email static class
Code Snippet
  1. // Construct recipient from form
  2. Recipient recipient = new Recipient() { Name = FriendName, Email = FriendEmail };
  3.  
  4. // Send email
  5. EmailHelper.SendEmail(Server.MapPath("~/EmailTemplate"), recipient);
End of Code Snippet


Note: I have added an email template path as a parameter. This is so that if you execute the email helper from a web application, it knows where to find the templates!


Sample HTML Template File
Code Snippet
  1. <body style="background: #000000;">
  2. Hello <%NAME%>,
  3. <br /><br />
  4. I thought i'd send you a picture of some guy holding an envelope.
  5. <br /><br />
  6. <img src="cid:email" />
  7. <br /><br />
  8. I have no idea why though!
  9. <br /><br />
  10. Thanks!
  11. </body>
End of Code Snippet


Note:
Notice the cid prefix for the images. This indicates a contentID for a linked resource. If you look in the code for the email helper, you will notice that I am created 1 linked resource and setting the contentID to 'email'. This will simply embed the image.
Again, in this template, you will notice I have used a custom tag for the friend's name called NAME. This is replaced within the email helper when we construct a ListDictionary and add our replacements to it.


and thats it!!

If you stuck, or can't get it to compile, then download the full sample here

MVC3 Web Application - Full Email Example with Fallback

1 comment:

ARE A EM said...

The link to the full project is broken; any chance that can be restored?

Also can you please consider revising this or publishing a separate article for VS2013/.Net 4.5 ?