Click here to Skip to main content
Click here to Skip to main content

Email Templates

, 29 Jul 2007
Rate this:
Please Sign up or sign in to vote.
In this article, I am going to consider the process of managing and sending emails inside a .NET application.

Introduction

In this article, I am going to consider the process of managing and sending emails inside a Web application. Almost every Web site has a feedback form or a registration form. After validating user's entries and doing some actions, it is necessary sometimes to send a notification email to the user or, for instance, to a moderator of the site. To achieve this goal, you can use the standard .NET classes (read more here and here if you are using .NET 2.0). But how can you manage permanent content of emails inside your Web application?

The simplest way is to hardcode email content and insert dynamic values using concatenating functions inside page's code. But what if you need to change design of email or add some functionality to the page source? Email template can be very big and contain a pile of dynamic variables with concatenation correspondingly. Looking into such source is horrible. Everybody knows it and in spite of this, even professional programmers often choose the way to place content email in the source code.

Summarizing all problems, you can be faced with:

  • Source of page that sends email is hardly readable because there are a lot of long string literals and a lot of concatenate operations
  • You cannot preview email template until you actually run the code
  • It's necessary to recompile the application after even a small content change in email template

Solution

The solution of the problem is simple – using email templates and parser. For example, we need to send email to a user after registration is completed. This email must contain such information like first and last name to say hello and in addition we need to send login information too. Example template can be similar to this one:

Hello ##FirstName## ##LastName##,

You have been registered with the following information:

Login: ##Login##
Password: ##Password##.

To login use this link: www.server.com

##FirstName## in the body of the message does mean a marker of place in the template where parser inputs a value of FirstName variable. So we have four places where we need to place personal information for each user. Other contents are permanent. Also please notice that we can use HTML as content for template instead of plain text in the previous example:

<html>
<head>
<style>
p {
 font-family: Verdana;
}
</style>
</head>
<body>
<p>Hello ##FirstName## ##LastName##,</p>
<p>You have been registered with the following information:</p><table>
<tr>
<td>Login: </td>
<td>##Login##</td>
</tr>
<tr>
<td>Password: </td>
<td>##Password##.</td>
</tr>
</table><p>To login use this link: 
	<a href="http://www.server.com/">www.server.com</a></p>
</body>

To use this template in your code, we need to save it in a separated file. I called it as Registration.htm and placed it in MailTemplates folder where I can manage all application mail templates. Now we can use the registration template in code:

Dim templateVars As New Hashtable()
templateVars.Add("FirstName", "Alexander")
templateVars.Add("LastName", "Kleshchevnikov")
templateVars.Add("Login", "seigo")
templateVars.Add("Password", "123pass")
Dim parser As New Parser_
	(Server.MapPath("~/MailTemplates/Registration.htm"), templateVars)
Try
    Dim message As New MailMessage()
    message.From = New MailAddress("<a href="mailto:noreply@server.com">noreply@server.com</a>")
    message.To.Add(New MailAddress("<a href="mailto:seigo.ua@via.com.ua">seigo.ua@via.com.ua</a>"))
    message.Subject = "Registration"
    message.BodyEncoding = System.Text.Encoding.UTF8
    message.Body = parser.Parse
    message.IsBodyHtml = True
    Dim client As New SmtpClient()
    client.Send(message)
    Label1.Text = "Email has been sent."
Catch ex As Exception
    Label1.Text = "Could not send email: " + ex.Message
End Try

The first key element of sending code is creating templateVars object as instance of Hashtable class from System.Collections and pushing all mentioned variables in the template with key equal variable name. After that, we can create a parser object as an instance of Parser class passing template path and hashtable with template variables as parameters to the constructor. To get a template with replaced values, you can simply invoke the Parse method of the Parser object.

To configure SMTP server settings, add configuration section in web.config file:

<configuration>
  ...
  <system.net>
    <mailSettings>
      <smtp deliveryMethod="Network">
        <network defaultCredentials="true" host="localhost" port="25" />
      </smtp>
    </mailSettings>
  </system.net>
  ...
</configuration>

For more information, read this MSDN article.

Modificators

Now let's imagine you need to replace all unsafe HTML special chars with HTML-encoded equivalents to prevent a user from using tags in first name or somewhere and destroy view of HTML email message. Of course, you can use Server.HTMLEncode() method and rewrite adding variables in the hashtable:

templateVars.Add("FirstName", Server.HtmlEncode("Alexander"))
templateVars.Add("LastName", Server.HtmlEncode("Kleshchevnikov"))
templateVars.Add("Login", Server.HtmlEncode("seigo"))
templateVars.Add("Password", Server.HtmlEncode("123pass"))

But after that, we need to recompile the application. And it will hang up application performance if we need to make it on the server directly. Another approach is using value modificators – simple functions which have variable value as one of the parameters. For example:

...
<p>Hello ##FirstName:HTMLENCODE## ##LastName:HTMLENCODE##,</p>
...

Now if the user inputs "<b>Alex</b>" as first name, the value actually will be inserted in the template as "&lt;b&gt;Alex&lt;/b&gt;". And we do not need to change and recompile application sources. In the table below, you can find all built-in value modificators with a short functional description:

NL2BR Replace all new line chars with <br /> tag.
HTMLENCODE Change all unsafe HTML special chars with HTML-encoded equivalents.
UPPER Change string to uppercase.
LOWER Change string to lowercase.
TRIM Remove all spaces from right and left side.
TRIMEND Remove spaces from end only.
TRIMSTART Remove spaces from start only.

If it is not enough for your claims, you can create custom value modificators. For instance, if you need to create a modificator to change only the first letter to upper (to represent names) you can create such a class:

Imports System
Imports System.Collections

Namespace TemplateParser.Modificators
    Class NL2BR
        Inherits Modificator

        Public Overrides Sub Apply(ByRef Value As String, _
			ByVal ParamArray Parameters() As String)
            Value = Value.ToUpper()(0) + Value.ToLower().Substring(1)
        End Sub
    End Class
End Namespace

Compile it in assembly and place in the bin folder. Using Reflection Parser object will determine that you declared a new custom FIRSTUPPER modificator and will use it as soon as it finds a link to it from the template.

Conditional Statements

Next I am going to explain how to use conditions in email templates. There can be such a situation when depending on some variable value we need to use one or the other block of template. A simple example is show "Please sign in" message if user is not logged into a system or show greeting with full name in other case:

##If--IsRegisteredUser##
<p>Hello, ##FirstName## ##LastName##!</p>
##Else--IsRegisteredUser##
Please sign in.
##EndIf--IsRegisteredUser##

IsRegisteredUser must contain a boolean variable which determines user login status. Please notice also that you can use variables FirstName and LastName in conditional blocks.

Blocks and Loops

Finally, I want to show how to display a list of data in template using Block declaration. You can be faced with a problem when you need to place some list of data in a template which rows you need to generate in code. For example - list of links like this one:

<ul>
    <li><a href="http://www.codeproject.com">Codeproject</a></li>
    <li><a href="http://www.msdn.com">MSDN</a></li>
    <li><a href="http://www.google.com">Google</a></li>
</ul>

The problem is we do not know how many links we have but it's clear that we can declare iterative block and use it in a loop. In the following situation, such block is <li> tag and everything that it includes. So we can put it in a Block:

##BlockBegin--Link##
<li><a href="##Url##">##Title##</a></li>
##BlockEnd--Link##

I called Block as "Link" and replaced two areas with ##Url## and ##Title## variables. Now I can iterate through Hashtable with links data and create template block with a list of all links:

Dim links As New Hashtable()
links.Add("Google", "http://www.google.com/")
links.Add("Codeproject", "http://www.codeproject.com/")
links.Add("MSDN", "http://www.msdn.com/")

Dim linksBlock As New StringBuilder()
Dim myEnumerator As IDictionaryEnumerator = links.GetEnumerator()
While myEnumerator.MoveNext()
    Dim blockVars As New Hashtable()
    blockVars.Add("Url", myEnumerator.Value)
    blockVars.Add("Title", myEnumerator.Key)
    linksBlock.Append(parser.ParseBlock("Link", blockVars))
End While

To get parsed content of each link, I use the ParseBlock() method. The first parameter is the name of the Block and the second is Hashtable of variables which is needed to replace in the current Block.

Summary

Using template parser, you can simply store and manage all application email templates in one place. This approach helps separate content from code sources and gives you the ability to edit them easily.

History

  • 16/8/2006 - Some fixes - Added ability to declare and use Blocks and use them to create a list of data. Convert sources of .NET 2.0 version to VB.NET.
  • 7/29/2007 - Added sources for .NET 1.1.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Alexander Kleshchevnikov
Web Developer
Ukraine Ukraine
Alexander is freelance web developer with 4 years experience. He has skills in ASP.NET/MS SQL Server, PHP/MySQL, Ruby On Rails, XHTML, CSS. You can contact with me by seigo.ua@gmail.com. Also check my homepage at www.klalex.com.

Comments and Discussions

 
QuestionHow to include nested conditional blocks in the code.... PinmemberMember 347355313-Nov-09 5:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 29 Jul 2007
Article Copyright 2006 by Alexander Kleshchevnikov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid