Using Email Templates With ASP.NET

Somedays working with ASP.NET can be a treat. Especially on those days when you stumble upon some functionality you had no idea existed.

It seems like every website has some need to generate emails for things like order confirmation, password resets, or news updates. System.Net.Mail provides excellent functionality for building email messages and communicating with SMTP servers. But it is up to the developer to build the actual content of the message programmatically, usually by using a StringBuilder to concatenate a bunch of user-specific and common strings.

Turns out there is class and technique that can simplify that code quite a bit. MailDefinition is a class that allows you to build email templates and easily update the dynamic parts.

Check out the following code:

public void GenerateOrderEmailCustomer(Order order)
{
   try
   {
      MailDefinition template = new MailDefinition();
      template.BodyFileName = "~/templates/CustomerOrderConfirmation.html";
      template.From = order.Store.Retailer.ApplicationConfig.EmailOrderFromAddress;

      Dictionary<string, string> data = new Dictionary<string, string>();
      data.Add("<<order.OrderNumber>>", order.OrderNumber.ToString());
      data.Add("<<order.Store.Name>>", order.Store.Name);
      data.Add("<<order.Status>>", order.Status.ToString());
      data.Add("<<order.FirstName>>", order.FirstName);
      data.Add("<<order.LastName>>", order.LastName);
      data.Add("<<order.Address1>>", order.Address1);
      data.Add("<<order.Address2>>", order.Address2);
      data.Add("<<order.City>>", order.City);
      data.Add("<<order.State>>", order.State);
      data.Add("<<order.Zip>>", order.Zip);
      data.Add("<<order.PaymentMethod>>", order.PaymentMethod.ToString());
      data.Add("<<order.Payment.CardHoldersName>>", (order.Payment != null) ? order.Payment.CardHoldersName:"n/a");
      data.Add("<<order.Payment.CardType>>", (order.Payment != null)? order.Payment.CardType:"n/a");
      data.Add("<<order.Payment.CreditCardNumber>>", (order.Payment != null) ? order.Payment.CreditCardNumber.Substring(0, 4) + " **** **** ****" : "n/a");

      string detailTable = "";
      foreach (OrderDetail detail in order.Details)
      {
         detailTable += string.Format("<tr><td>{0}</td><td>{1}</td></tr>", detail.Quantity, detail.Name);
      }

      data.Add("<<order.Details>>", detailTable);
      data.Add("<<url>>", "http://www.wineconnect.com/");

      MailMessage message = template.CreateMailMessage(order.Email, data, new LiteralControl());
      message.IsBodyHtml = true;
      message.Subject = string.Format("Order Confirmation: {0} ({1})", order.Store.Name, order.OrderNumber.ToString());

      SmtpClient client = new SmtpClient(order.Store.Retailer.ApplicationConfig.EmailServer, order.Store.Retailer.ApplicationConfig.EmailPort);

      //Enable SSL
      client.EnableSsl = order.Store.Retailer.ApplicationConfig.EmailEnableSsl;

      client.UseDefaultCredentials = false;
      client.Credentials = new NetworkCredential(order.Store.Retailer.ApplicationConfig.EmailUsername, order.Store.Retailer.ApplicationConfig.EmailPassword);

      client.SendCompleted += new SendCompletedEventHandler(MailDeliveryComplete);
      client.SendAsync(message, message);
   }
   catch (Exception ex)
   {
      _log.Error("Error caught while sending customer order confirmation", ex);
   }
}

Combined with this template:

<html>
<head>
<title>Order Confirmation</title>
</head>
<body>
<table width="400" border="1" cellpadding="4" cellspacing="0" bordercolor="EDF5E3">
<tr bgcolor="#999999" >
<td align="center" style="height: 16px"><span color="#FFFFFF">Order Confirmation (<<order.OrderNumber>>)</span></td>
</tr>
<tr><td>
<<order.FirstName>>,<br><br>
This email confirms your order: <br>
Store: <<order.Store.Name>><br><br>
Order Status: <<order.Status>><br>
<br>Billing Information: <br>
Customer: <<order.FirstName>> <<order.LastName>><br>
Address1: <<order.Address1>><br>
Address2: <<order.Address2>><br>
City: <<order.City>><br>
State: <<order.State>><br>
Zip: <<order.Zip>><br>
<br>Payment Method: <<order.PaymentMethod>><br>
Card Holder: <<order.Payment.CardHoldersName>><br>
Card Type: <<order.Payment.CardType>><br>
Card Number: <<order.Payment.CreditCardNumber>><br>
Order Information:
<table class="messageTxt"><tr><td>Qty.</td><td>Item</td></tr>
<<order.Details>>
</table><br/>
Your order number is <strong><a href='<<url>>/OrderSummary.aspx?OrderNumber=<<order.OrderNumber>>'><<order.OrderNumber>></a></strong>.
Please feel free to contact us with any questions or visit our website if you need directions to our store.<br><br>
<a href="<<url>>"><strong><<url>></strong></a><br><br>
Thank you,<br><br>Wine Store Dudes<br></td></tr>
</table>
</body>
</html>

It generates this email:

CropperCapture[43]

Good Stuff:

Using this technique I was able to get rid of a bunch of string concatenation and string.Format code. The template should be much easier to maintain going forward.

Lame:

The way MailDefinition replaces the dynamic tokens is nice but a bit limiting. It might be fun to try using a Server.Execute method to pick up other ASP.NET goodness like data binding and advanced logic.

The CreateMailMessage function also requires a OwnerControl to be passed in. They could have given us an overloaded version that didn’t require this because I doubt anyone ever uses it.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • bodytext
  • del.icio.us
  • Facebook
  • Google
  • Reddit
  • StumbleUpon
  • TwitThis

Leave a Reply