I've been in at least three jobs now where a significant amount of effort by others on the team was spent performing e-mail blasts to thousands or tens of thousands of [opted-in] users, whether for newsletters or for e-mail advertising campaigns. And I've also had countless encounters of requirements of a web site to send an e-mail to a single user, such as upon registration or user-to-user communications via web-based PM interfaces.
It has been surprising to me to discover how insistent development teams are with using alternative templating techniques to generate the pre-formatted e-mails, everything from:
- .txt files with variable markers like {{USERNAME}}
- NVelocity templates
- Database records with text BLOBs and variable markers like %%USERNAME%%
- C#-written code with lots and lots of myStringBuilder.Append()'s.
(I do like the database text BLOBs if only for simple plain-text formatted e-mails, as this sort of thing allows non-developer system administrators to customize their templates on the fly.)
Yet, never do people take advantage of the templating system they're already using. It's called ASP.NET.
Why would you use ASP.NET? Actually, in a web context, why wouldn't you might be the fairer question, because reasons for using it are obvious:
- ASP.NET is one of the most powerful and versatile templating engines in use today. Why anyone would use NVelocity in a world where there's ASP.NET is beyond me.
- You can take advantage of the full .NET Framework, web services, HttpContext features, and more.
- You can data-bind to the database and output the same user-customized Rich HTML as you could with normal web pages.
- You can add repeaters and other multi-dimensional variables that are not possible with flat templates.
Cons:
Say, for instance, you're invoking it from a web page that has just processed some database transaction and you need to e-mail the user to inform him of a status or reminder or something. You can use Server.Execute() to invoke the ASP.NET template, which will read the results back into a Stream object. Pass a MemoryStream into it as a parameter, then flush that out into a StreamReader to read it into a System.String. Easy! What about context and state? All the more reason to use Server.Execute and not System.Net.WebClient. You can retain state, although in any case you can also build up a lengthy query string in your URL to be invoked, and then on the template you can perform whatever context synchronization that needs to be performed.
Here's a sample that demonstrates how (but not why).
Default.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<%=TemplateOutput %>
</div>
</form>
</body>
</html>
Default.aspx.cs:
protected string TemplateOutput
{
get
{
MemoryStream ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms);
Context.Items["Question"] =
"Is this a working template?";
Server.Execute("Template.aspx", sw, true);
sw.Flush();
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
return sr.ReadToEnd();
}
}
Template.aspx:
<%@ Page Language="C#" %>
<%= Context.Items["Question"].ToString()
.Replace("Is this", "This is").Replace("?", "!") %>
Output:
This is a working template!
I hope this was helpful.
