views:

13105

answers:

19

I'm working on a site that will send out a significant number of emails. I want to set up both header and footer text, or maybe even templates to allow the users to easily edit these emails if they need to.

If I embed the HTML inside C# string literals, it's ugly and they would have to worry about escaping. Including flat files for the header and footer might work, but something about it just doesn't feel right.

What would be ideal what be to use a .ASPX page as a template somehow, then just tell my code to serve that page, and use the HTML returned for the email.

Is there a nice and easy way to do this? Is there a better way to go about solving this problem?

Updated:
I added an answer that enables you to use a standard .aspx page as the email template. Just replace all the variables like you normally would, use databinding, etc. Then just capture the output of the page, and voila! You have your HTML email!

UPDATED WITH CAVEAT!!!:
I was using the MailDefinition class on some aspx pages just fine, but when trying to use this class during a server process that was running, it failed. I believe it was because the MailDefinition.CreateMailMessage() method requires a valid control to reference, even though it doesn't always do something. Because of this, I would recommend my approach using an aspx page, or Mun's approach using an ascx page, which seems a little better.

+1  A: 

Sure you can create an html template and I would recommend also a text template. In the template you can just put [BODY] in the place where the body would be placed and then you can just read in the template and replace the body with the new content. You can send the email using .Nets Mail Class. You just have to loop through the sending of the email to all recipients after you create the email initially. Worked like a charm for me.

using System.Net.Mail;

// Email content
string HTMLTemplatePath = @"path";
string TextTemplatePath = @"path";
string HTMLBody = "";
string TextBody = "";

HTMLBody = File.ReadAllText(HTMLTemplatePath);
TextBody = File.ReadAllText(TextTemplatePath);

HTMLBody = HTMLBody.Replace(["[BODY]", content);
TextBody = HTMLBody.Replace(["[BODY]", content);

// Create email code
MailMessage m = new MailMessage();

m.From = new MailAddress("[email protected]", "display name");
m.To.Add("[email protected]");
m.Subject = "subject";

AlternateView plain = AlternateView.CreateAlternateViewFromString(_EmailBody + text, new System.Net.Mime.ContentType("text/plain"));
AlternateView html = AlternateView.CreateAlternateViewFromString(_EmailBody + body, new System.Net.Mime.ContentType("text/html"));
mail.AlternateViews.Add(plain);
mail.AlternateViews.Add(html);

SmtpClient smtp = new SmtpClient("server");
smtp.Send(m);
jmein
You can cut out the StreamReader stuff and replace with File.ReadAllText(path)
John Sheehan
thanks John I hadnt thought about that
jmein
This is a good start, but only provides functionality for a header and footer. This doesn't really help with the body itself.
SkippyFire
The body all you have to do is enter the body content that is desired in to the HTMLBody and TextBody Fields or you can of course store them in files as well
jmein
A: 

If you are able to allow the ASPNET and associated users permission to read & write a file, you can easily use an HTML file with standard String.Format() placeholders ({0}, {1:C}, etc.) to accomplish this.

Merely read in the file, as a string, using classes from the System.IO namespace. Once you have that string, pass it as the first argument to String.Format(), and provide the parameters.

Keep that string around, and use it as the body of the e-mail, and you're essentially done. We do this on dozens of (admittedly small) sites today, and have had no issues.

I should note that this works best if (a) you're not sending zillions of e-mails at a time, (b) you're not personalizing each e-mail (otherwise you eat up a ton of strings) and (c) the HTML file itself is relatively small.

John Rudy
+20  A: 

You could try the MailDefinition class

John Sheehan
That is a feature I'd never heard of ... good call!
John Rudy
Ditto, thank you.
domus.vita
I just want to point out that this is good for basic emails, but not anything complex. The MailDefinition class does not support databinding. The only thing it really does is offer string replacements. Although, its also built into the Membership Account Creation Wizard.
SkippyFire
+7  A: 

If you want to pass parameters like user names, product names, ... etc. you can use open source template engine NVelocity to produce your final email / HTML's.

An example of NVelocity template (MailTemplate.vm) :

A sample email template by <b>$name</b>.
<br />

Foreach example :
<br />    
#foreach ($item in $itemList)

[Date: $item.Date] Name: $item.Name, Value: $itemValue.Value
<br /><br />

#end

Generating mail body by MailTemplate.vm in your application :

VelocityContext context = new VelocityContext();
context.Put("name", "ScarletGarden");
context.Put("itemList", itemList);

StringWriter writer = new StringWriter();

Velocity.MergeTemplate("MailTemplate.vm", context, writer);

string mailBody = writer.GetStringBuilder().ToString();

The result mail body is :

A sample email template by ScarletGarden.

Foreach example :

[Date: 12.02.2009] Name: Item 1, Value: 09

[Date: 21.02.2009] Name: Item 4, Value: 52

[Date: 01.03.2009] Name: Item 2, Value: 21

[Date: 23.03.2009] Name: Item 6, Value: 24

For editing the templates, maybe you can use FCKEditor and save your templates to files.

Canavar
+1  A: 

What would be ideal what be to use a .ASPX page as a template somehow, then just tell my code to serve that page, and use the HTML returned for the email.

You could easily just construct a WebRequest to hit an ASPX page and get the resultant HTML. With a little more work, you can probably get it done without the WebRequest. A PageParser and a Response.Filter would allow you to run the page and capture the output...though there may be some more elegant ways.

Mark Brackett
+17  A: 

You might also want to try loading a control, and then rendering it to a string and setting that as the HTML Body:

// Declare stringbuilder to render control to
StringBuilder sb = new StringBuilder();

// Load the control
UserControl ctrl = (UserControl) LoadControl("~/Controls/UserControl.ascx");

// Do stuff with ctrl here

// Render the control into the stringbuilder
StringWriter sw = new StringWriter(sb);
Html32TextWriter htw = new Html32TextWriter(sw);
control.RenderControl(htw);

// Get full body text
string body = sb.ToString();

You could then construct your email as usual:

MailMessage message = new MailMessage();
message.From = new MailAddress("[email protected]", "from name");
message.Subject = "Email Subject";
message.Body = body;
message.BodyEncoding = Encoding.ASCII;
message.IsBodyHtml = true;

SmtpClient smtp = new SmtpClient("server");
smtp.Send(message);

You user control could contain other controls, such as a header and footer, and also take advantage of functionality such as data binding.

Mun
I somehow missed this answer the first time around... nice one. Similar to my solution, but with an ascx instead of an aspx. I still think aspx would be better, since it would offer a complete page, instead of a control, but that's just what I think.
SkippyFire
Yep, you could use either solution... They work in the same way. One benefit of this approach is consistency. For example, you could show a user an order summary and include exactly the same thing in the confirmation email by reusing the same control.
Mun
Minor point but you're missing a line to declare a StringBuilder in the first code block.
Kirschstein
@Kirschstein Thanks, fixed.
Mun
The example doesn't explain where the code resides in, is it a page?, because LoadControl is a page/control method.
Shrage Smilowitz
A: 

Set the set the Email Message IsBodyHtml = true

Take your object that contains your email contents Serialize the object and use xml/xslt to generate the html content.

If you want to do AlternateViews do the same thing that jmein only use a different xslt template to create the plain text content.

one of the major advantages to this is if you want to change your layout all you have to do update the xslt template.

Bob The Janitor
+2  A: 

I think you could also do something like this:

Create and .aspx page, and put this at the end of the OnLoad method, or call it manually.

    StringBuilder sb = new StringBuilder();
    StringWriter sw = new StringWriter(sb);
    HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
    this.Render(htmlTW);

I'm not sure if there are any potential issues with this, but it looks like it would work. This way, you could use a full featured .aspx page, instead of the MailDefinition class which only supports Text replacements.

SkippyFire
While the MailDefinition class is a good start, its a bit rudimentary. This method should support a lot more features like databinding, and maybe even tracing. Any thoughts on this, or potential gotchas?
SkippyFire
I've done something similar to this. Would recommend.
Kieran Benton
Great! Did you have any issues with it?
SkippyFire
So you're going to let your users edit the .aspx files when they need to make changes to the mail template? I'd call that a potential issue.
Bryan
I wouldn't think so, at least, no more of a risk than other templates they could edit. Granted, if they knew what they were doing, they could cause harm, but in this case at least, its unlikely. It wouldn't be a complex .aspx page, more of a template with placeholders.
SkippyFire
+4  A: 

If flexibility is one of your prerequisites, XSLT might be a good choice, which is completely supported by .NET framework and you would be able to even let the user edit those files. This article (http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/) might be useful for a start (msdn has more info about it). As said by ScarletGarden NVelocity is another good choice but I do prefer XSLT for its " built-in" .NET framework support and platform agnostic.

Everton
+1  A: 

i had a similar requirement on 1 of the projects where you had to send huge number of emails each day, and the client wanted complete control over html templates for different types of emails.

due to the large number of emails to be sent, performance was a primary concern.

what we came up with was static content in sql server where you save entire html template mark up (along with place holders, like [UserFirstName], [UserLastName] which are replaced with real data at run time) for different types of emails

then we loaded this data in asp.net cache - so we dont read the html templates over and over again - but only when they are actually changed

we gave the client a WYSIWYG editor to modify these templates via a admin web form. whenever updates were made, we reset asp.net cache.

and then we had a seperate table for email logs - where every email to be sent was logged. this table had fields called emailType, emailSent and numberOfTries.

we simply ran a job every 5 minutes for important email types (like new member sign up, forgot password) which need to be sent asap

we ran another job every 15 minutes for less important email types (like promotion email, news email, etc)

this way you dont block your server sending non stop emails and you process mails in batch. once an email is sent you set the emailSent field to 1.

Raj
But how did you handle collections?
Riri
I have done this also and it worked well. Plus you can historically go back and see the records of emails sent, if reports are your thing.
Mark Glorie
+2  A: 

Be careful when doing this, SPAM filters seem to block ASP.net generated html, apparently because of ViewState, so if you are going to do this make sure the Html produced is clean.

I personally would look into using Asp.net MVC to achieve your desired results. or NVelocity is quite good at this

danswain
good point dan ;-)
Raj
+1  A: 

Look at SubSonic (www.subsonicproject.com). They're doing exactly this to generate code - the template is standard ASPX, and it outputs c#. The same method would be reusable for your scenario.

jvenema
A: 

I'd use a templating library like TemplateMachine. this allows you mostly put your email template together with normal text and then use rules to inject/replace values as necessary. Very similar to ERB in Ruby. This allows you to separate the generation of the mail content without tying you too heavily to something like ASPX etc. then once the content is generated with this, you can email away.

MikeJ
A: 

I like Raj's answer. Programs like ListManager & frameworks like DNN do similar things, and if easy editing by non-technical users is required, WYSIWYG editors to modify HTML stored in SQL is a mostly easy, straightforward way to go and can easily accommodate editing headers independently from footers, etc, as well as using tokens to dynamically insert values.

One thing to keep in mind if using the above method (or any, really) is to be strict and careful about which types of styling and tags you allow the editors to insert. If you think browsers are finicky, just wait until you see how differently email clients render the same thing...

Nick
+3  A: 

Here is one more alternative that uses XSL transformations for more complex email templates: Sending HTML-based email from .NET applications.

Alek Davis
+1  A: 

Note that the aspx and ascx solutions require a current HttpContext, so cannot be used asynchronously (eg in threads) without a lot of work.

Rosco
A: 

Similar to Canavar's answer, but instead of NVelocity, I always use "StringTemplate" which I load the template from a configuration file, or load an external file using File.ReadAllText() and set the values.

It's a Java project but the C# port is solid and I've used it in several projects (just used it for email templating using the template in an external file).

Alternatives are always good.

Bryan Bailliache
+2  A: 

Mail.dll email component includes email template engine:

Here's the syntax overview:

<html>
<body>
Hi {FirstName} {LastName},

Here are your orders: 
{foreach Orders}
    Order '{Name}' sent to <strong>{Street}</strong>. 
{end}

</body>
</html>

And the code that loads the template, fills data from c# object and sends an email:

Mail.Html(Template
              .FromFile("template.txt")
              .DataFrom(_contact)
              .Render())
    .Text("This is text version of the message.")
    .From(new MailBox("[email protected]", "Alice"))
    .To(new MailBox("[email protected]", "Bob"))
    .Subject("Your order")
    .UsingNewSmtp()
    .WithCredentials("[email protected]", "password")
    .Server("mail.com")
    .WithSSL()
    .Send();

You can get more info on email template engine blog post.

Or just download Mail.dll email component and give it a try.

Please note that this is a commercial product I've created.

Pawel Lesnikowski
A: 

I come up with following solution to send HTML Template based email containing some Dynamic data. I downloaded a open source parser vici.core. This is great tool and has lot of features HTML Template parser is just one on them. More details on my blog.

Waqar Ahmad Bhatti