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:
{
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:
<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:
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.

October 22nd, 2008 at 5:14 pm
This is a great intro to mail templates and a good sample. I’ve been using user controls to create templates, and adding for the variables, which works fine (except you lose your specific-line errors in debugging) which allows for the data binding and advanced logic you mentioned.
January 19th, 2009 at 10:51 pm
Thanks for sharing this. You saved a lot of time for me
March 14th, 2009 at 6:55 am
It’s the first time I commented here and I must say you provide genuine, and quality information for bloggers! Good job.
p.s. You have a very good template for your blog. Where have you got it from?
December 15th, 2009 at 2:36 am
thanks very usefull information !