Is there a better way to generate HTML email in C# (for sending via System.Net.Mail), than using a Stringbuilder to do the following:
string userName = "John Doe";
StringBuilder mailBody = new StringBuilder();
mailBody.AppendFormat("<h1>Heading Here</h1>");
mailBody.AppendFormat("Dear {0}," userName);
mailBody.AppendFormat("<br />");
mailBody.AppendFormat("<p>First part of the email body goes here</p>");
and so on, and so forth?
You can use the MailDefinition class.
This is how you use it:
MailDefinition md = new MailDefinition();
md.From = "test#domain.example";
md.IsBodyHtml = true;
md.Subject = "Test of MailDefinition";
ListDictionary replacements = new ListDictionary();
replacements.Add("{name}", "Martin");
replacements.Add("{country}", "Denmark");
string body = "<div>Hello {name} You're from {country}.</div>";
MailMessage msg = md.CreateMailMessage("you#anywhere.example", replacements, body, new System.Web.UI.Control());
Also, I've written a blog post on how to generate HTML e-mail body in C# using templates using the MailDefinition class.
Use the System.Web.UI.HtmlTextWriter class.
StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);
html.RenderBeginTag(HtmlTextWriterTag.H1);
html.WriteEncodedText("Heading Here");
html.RenderEndTag();
html.WriteEncodedText(String.Format("Dear {0}", userName));
html.WriteBreak();
html.RenderBeginTag(HtmlTextWriterTag.P);
html.WriteEncodedText("First part of the email body goes here");
html.RenderEndTag();
html.Flush();
string htmlString = writer.ToString();
For extensive HTML that includes the creation of style attributes HtmlTextWriter is probably the best way to go. However it can be a bit clunky to use and some developers like the markup itself to be easily read but perversly HtmlTextWriter's choices with regard indentation is a bit wierd.
In this example you can also use XmlTextWriter quite effectively:-
writer = new StringWriter();
XmlTextWriter xml = new XmlTextWriter(writer);
xml.Formatting = Formatting.Indented;
xml.WriteElementString("h1", "Heading Here");
xml.WriteString(String.Format("Dear {0}", userName));
xml.WriteStartElement("br");
xml.WriteEndElement();
xml.WriteElementString("p", "First part of the email body goes here");
xml.Flush();
Updated Answer:
The documentation for SmtpClient, the class used in this answer, now reads, 'Obsolete("SmtpClient and its network of types are poorly designed, we strongly recommend you use https://github.com/jstedfast/MailKit and https://github.com/jstedfast/MimeKit instead")'.
Source: https://www.infoq.com/news/2017/04/MailKit-MimeKit-Official
Original Answer:
Using the MailDefinition class is the wrong approach. Yes, it's handy, but it's also primitive and depends on web UI controls--that doesn't make sense for something that is typically a server-side task.
The approach presented below is based on MSDN documentation and Qureshi's post on CodeProject.com.
NOTE: This example extracts the HTML file, images, and attachments from embedded resources, but using other alternatives to get streams for these elements are fine, e.g. hard-coded strings, local files, and so on.
Stream htmlStream = null;
Stream imageStream = null;
Stream fileStream = null;
try
{
// Create the message.
var from = new MailAddress(FROM_EMAIL, FROM_NAME);
var to = new MailAddress(TO_EMAIL, TO_NAME);
var msg = new MailMessage(from, to);
msg.Subject = SUBJECT;
msg.SubjectEncoding = Encoding.UTF8;
// Get the HTML from an embedded resource.
var assembly = Assembly.GetExecutingAssembly();
htmlStream = assembly.GetManifestResourceStream(HTML_RESOURCE_PATH);
// Perform replacements on the HTML file (if you're using it as a template).
var reader = new StreamReader(htmlStream);
var body = reader
.ReadToEnd()
.Replace("%TEMPLATE_TOKEN1%", TOKEN1_VALUE)
.Replace("%TEMPLATE_TOKEN2%", TOKEN2_VALUE); // and so on...
// Create an alternate view and add it to the email.
var altView = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
msg.AlternateViews.Add(altView);
// Get the image from an embedded resource. The <img> tag in the HTML is:
// <img src="pid:IMAGE.PNG">
imageStream = assembly.GetManifestResourceStream(IMAGE_RESOURCE_PATH);
var linkedImage = new LinkedResource(imageStream, "image/png");
linkedImage.ContentId = "IMAGE.PNG";
altView.LinkedResources.Add(linkedImage);
// Get the attachment from an embedded resource.
fileStream = assembly.GetManifestResourceStream(FILE_RESOURCE_PATH);
var file = new Attachment(fileStream, MediaTypeNames.Application.Pdf);
file.Name = "FILE.PDF";
msg.Attachments.Add(file);
// Send the email
var client = new SmtpClient(...);
client.Credentials = new NetworkCredential(...);
client.Send(msg);
}
finally
{
if (fileStream != null) fileStream.Dispose();
if (imageStream != null) imageStream.Dispose();
if (htmlStream != null) htmlStream.Dispose();
}
I use dotLiquid for exactly this task.
It takes a template, and fills special identifiers with the content of an anonymous object.
//define template
String templateSource = "<h1>{{Heading}}</h1>Dear {{UserName}},<br/><p>First part of the email body goes here");
Template bodyTemplate = Template.Parse(templateSource); // Parses and compiles the template source
//Create DTO for the renderer
var bodyDto = new {
Heading = "Heading Here",
UserName = userName
};
String bodyText = bodyTemplate.Render(Hash.FromAnonymousObject(bodyDto));
It also works with collections, see some online examples.
I would recomend using templates of some sort. There are various different ways to approach this but essentially hold a template of the Email some where (on disk, in a database etc) and simply insert the key data (IE: Recipients name etc) into the template.
This is far more flexible because it means you can alter the template as required without having to alter your code. In my experience your likely to get requests for changes to the templates from end users. If you want to go the whole hog you could include a template editor.
As an alternative to MailDefinition, have a look at RazorEngine https://github.com/Antaris/RazorEngine.
This looks like a better solution.
Attributted to...
how to send email wth email template c#
E.g
using RazorEngine;
using RazorEngine.Templating;
using System;
namespace RazorEngineTest
{
class Program
{
static void Main(string[] args)
{
string template =
#"<h1>Heading Here</h1>
Dear #Model.UserName,
<br />
<p>First part of the email body goes here</p>";
const string templateKey = "tpl";
// Better to compile once
Engine.Razor.AddTemplate(templateKey, template);
Engine.Razor.Compile(templateKey);
// Run is quicker than compile and run
string output = Engine.Razor.Run(
templateKey,
model: new
{
UserName = "Fred"
});
Console.WriteLine(output);
}
}
}
Which outputs...
<h1>Heading Here</h1>
Dear Fred,
<br />
<p>First part of the email body goes here</p>
Heading Here Dear Fred, First part of the email
body goes here
Emitting handbuilt html like this is probably the best way so long as the markup isn't too complicated. The stringbuilder only starts to pay you back in terms of efficiency after about three concatenations, so for really simple stuff string + string will do.
Other than that you can start to use the html controls (System.Web.UI.HtmlControls) and render them, that way you can sometimes inherit them and make your own clasess for complex conditional layout.
If you don't want a dependency on the full .NET Framework, there's also a library that makes your code look like:
string userName = "John Doe";
var mailBody = new HTML {
new H(1) {
"Heading Here"
},
new P {
string.Format("Dear {0},", userName),
new Br()
},
new P {
"First part of the email body goes here"
}
};
string htmlString = mailBody.Render();
It's open source, you can download it from http://sourceforge.net/projects/htmlplusplus/
Disclaimer: I'm the author of this library, it was written to solve the same issue exactly - send an HTML email from an application.
You might want to have a look at some of the template frameworks that are available at the moment. Some of them are spin offs as a result of MVC but that isn't required. Spark is a good one.
A commercial version which I use in production and allows for easy maintenance is LimiLabs Template Engine, been using it for 3+ years and allows me to make changes to the text template without having to update code (disclaimers, links etc..) - it could be as simple as
Contact templateData = ...;
string html = Template
.FromFile("template.txt")
.DataFrom(templateData )
.Render();
Worth taking a look at, like I did; after attempting various answers mentioned here.
Related
Maybe I'm not seeing the obvious here. I'm using this library for sending mails. And I'm also using transactional templates.
A quick code example:
var msg = new SendGridMessage {From = new EmailAddress("my#email.com")};
var personlization = new Personalization {Tos = new List<EmailAddress> {new EmailAddress("some#email.com")}};
dynamic t = new System.Dynamic.ExpandoObject();
t.firstname = "John";
t.lastname = "Doe";
t.message = "a <br/> line <br/> break";
msg.TemplateId = "abcdefg";
personlization.TemplateData = t;
msg.Personalizations = new List<Personalization> {personlization};
var response = await client.SendEmail(msg);
I can see how to add content manually, text or html by using the htmlContent prop, but in this case I'm using a transactional template.
In the example above, the email comes through html encoded instead of creating the line breaks, and I want the personalizations to be html. In addition, the template is html.
The answer your looking for is here, the issue is the template you are using
Handlebars HTML-escapes values returned by a {{expression}}. If you
don't want Handlebars to escape a value, use the "triple-stash", {{{.
Programming would be much easier without users...
What I really need to be able to do is:
Put the content of a web page (including styles) into the body of an email and also set the subject.
OR
Send the current user an email containing the body of a web page.
I really don't care how this is implemented -- server or client side. I've not come up with any good way of client side besides trying to push the web page into the clipboard for the users to then paste into their email.
App Background
I wrote a web site using c#, ts, angular. The site manages xml documents.
The users can select a document and click the "Human Readable" button or the "XML" button. The "Human Readable" is xml with xsl to make it look pretty for the humans. The XML button is apparently for non-humans.
The "Human Readable" version opens in another browser tab.
The users want a new "email" button for emailing the human readable. The person clicking the email button has access to my web site but the recipient may not.
I've attempted educating my users to do Ctrl+A, Ctrl+C, open email, Ctrl+V but this is beyond most of their capabilities.
I have tried so many different ways to accomplish this and all have failed.
I currently do a mailto link which opens their email and the body contains a link to the Human Readable.
Here's what I've tried so far -- this may not be a conclusive list of my attempts as I've been at this for a few days now.
I've tried putting a button in the human readable (xsl with javascript) in an attempt to copy the resulting html into the clipboard for the users to paste.
A button on the web site to scrape an iFrame into the clipboard
Many iterations of javascript copy/paste techniques
a c# controller that does a ReadAsStringAsync().Result function (which I will post below because I like that solution the best so far...
Option #4 I'm partial to and I got almost working -- if it weren't for that pesky xsl not getting formatted it would probably work. My results are the data being presented without xml tags and no styles.
[ActionName("PostEmailHumanReadable")]
public void EmailHumanReadable(List<DocumentVM> documents)
{
foreach (var document in documents)
{
var docId = document.document.DocId;
var docTypeId = document.document.DocTypeId;
var co = string.Empty;
var order = string.Empty;
var name = string.Empty;
var po = string.Empty;
// get the data for the subject line
using (var efUoW = new EFUnitOfWork(EDIEnvironment.EDIEnvironment.Instance.ConnectionString))
{
var doc = efUoW.DocumentRepository.GetById(docId);
co = doc.CompanyId.ToString();
var orders = efUoW.DocumentOrderRepository.GetByDocumentID(docId).ToList();
if (!string.IsNullOrEmpty(doc.Source_Order))
order = doc.Source_Order;
else
order = "n/a";
foreach (var o in orders)
order += string.Format("{0} ", o.OrderId);
//order = string.Join(",", doc.DocumentOrders.Select(q => q.OrderId).ToList());
name = doc.BillToName;
name = doc.PurchaseOrder;
}
var subject = string.Format("{0}, {1}, {2}, {3}", co, order, name, po);
// get the human readable
var hrResponse = GetFile(docId, docTypeId);
var hrText = hrResponse.Content.ReadAsStringAsync().Result;
// format the url
var url = string.Format("<a href='/api/Documents/Getfile?DocId={0}&DocTypeId={1}'>click here to open the jEDI Human Readable</a><br><br>", docId, docTypeId);
// find the current user's email address
var users = new List<string>();
users.Add(AppUser.ADUserName);
//var to = EmailUtility.GetEmailID(users);
// and finally send the email
EmailUtility.SendEmail(EmailUtility.GetEmailID(users), null, subject, url + hrText);
}
}
[ActionName("GetFile")]
public HttpResponseMessage GetFile(int DocId, int DocTypeId)
{
if (DocTypeId == (int)DocumentTypeEnum.EDI850)
{
using (var efUoW = new Factory_UOW().EF_UOW())
{
var doc = efUoW.DocumentRepository.GetById(DocId);
var xdoc = XDocument.Parse(doc.Message);
var proc = new XProcessingInstruction("xml-stylesheet", "type='text/xsl' href='/EDI850.xsl'");
xdoc.Root.AddBeforeSelf(proc);
var response = new HttpResponseMessage();
response.Content = new System.Net.Http.StringContent(xdoc.ToString());
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
response.Content.Headers.Add("X-UA-Compatible", "IE=edge");
return response;
}
I would be very grateful for any assistance in getting this to work.
And, yes, I know I shouldn't do the Async().Result -- blocking and all that... Let's just get this working first, shall we?
I have a program that sends emails utilizing templates via a web service. To test the templates, I made a simple program that reads the templates, fills it up with dummy value and send it. The problem is that the templates have different 'fill in' variable names. So what I want to do is open the template, make a list of the variables and then fill them with dummy text.
Right no I have something like:
StreamReader SR = new StreamReader(myPath);
.... //Email code here
Msg.Body = SR.ReadToEnd();
SR.Close();
Msg.Body = Msg.Body.Replace(%myFillInVariable%, "Test String");
....
So I'm thinking, opening the template, search for values in between "%" and put them in an ArrayList, then do the Msg.Body = SR.ReadToEnd(); part. Loop the ArrayList and do the Replace part using the value of the Array.
What I can't find is how to read the value between the % tags. Any suggestions on what method to use will be greatly appreciated.
Thanks,
MORE DETAILS:
Sorry if I wasn't clear. I'm passing the name of the TEMPLATE to the script from a drop down. I might have a few dozen Templates and they all have different %VariableToBeReplace%. So that's is why I want to read the Template with the StreamReader, find all the %value names%, put them into an array AND THEN fill them up - which I already know how to do. It's getting the the name of what I need to replace in code which I don't know what to do.
I am not sure on your question either but here is a sample of how to do the replacement.
You can run and play with this example in LinqPad.
Copy this content into a file and change the path to what you want. Content:
Hello %FirstName% %LastName%,
We would like to welcome you and your family to our program at the low cost of %currentprice%. We are glad to offer you this %Service%
Thanks,
Some Person
Code:
var content = string.Empty;
using(var streamReader = new StreamReader(#"C:\EmailTemplate.txt"))
{
content = streamReader.ReadToEnd();
}
var matches = Regex.Matches(content, #"%(.*?)%", RegexOptions.ExplicitCapture);
var extractedReplacementVariables = new List<string>(matches.Count);
foreach(Match match in matches)
{
extractedReplacementVariables.Add(match.Value);
}
extractedReplacementVariables.Dump("Extracted KeyReplacements");
//Do your code here to populate these, this part is just to show it still works
//Modify to meet your needs
var replacementsWithValues = new Dictionary<string, string>(extractedReplacementVariables.Count);
for(var i = 0; i < extractedReplacementVariables.Count; i++)
{
replacementsWithValues.Add(extractedReplacementVariables[i], "TestValue" + i);
}
content.Dump("Template before Variable Replacement");
foreach(var key in replacementsWithValues.Keys)
{
content = content.Replace(key, replacementsWithValues[key]);
}
content.Dump("Template After Variable Replacement");
Result from LinqPad:
I am not really sure that I understood your question but, you can try to put on the first line of the template your 'fill in variable'.
Something like:
StreamReader SR = new StreamReader(myPath);
String fill_in_var=SR.ReadLine();
String line;
while((line = SR.ReadLine()) != null)
{
Msg.Body+=line;
}
SR.Close();
Msg.Body = Msg.Body.Replace(fill_in_var, "Test String");
Can I set up HTML/Email Templates in C# on ASP.NET?
This question was asked and answered by SkippyFire and others...I have a follow up question. I like to keep things very simple, as a novice developer.
If I am not correct, Skippyfire said you could send the complete aspx page using this code:
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
this.Render(htmlTW);
Then just use net.mail to send on Page.Load event. This is very confusing to me. I can use this to render controls to an email.Body and send but I can not use this to load an entire page in anyway I have discovered.
Using Net.mail...
How would I send the page above? I tried to put nothing on the page but some text and send it using it's own page load event... I can not figure out any other way to send it from another page or button... (how would you do this? Wouldn't you have to somehow load the URL into an object?)... anyway I tried to do it from Page Load itself as Skippyfire describes in an old post and get this error from Visual studio IDE:
A page can have only one server-side Form tag.
Any help would be appreciated.
CS
It would be sometihng like this:
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter htmlTW = new HtmlTextWriter(sw))
{
this.Render(htmlTW);
}
using (var message = new MailMessage
{
From = new MailAddress("from#company.com"),
Subject = "This is an HTML Email",
Body = sw.ToString(),
IsBodyHtml = true
})
{
message.To.Add("toaddress1#company.com,toaddress2#company.com");
SmtpClient client = new SmtpClient();
client.Send(message);
}
}
There is another way to do this... You can host the ASP.Net runtime in your application. It's not major difficult either, this is "sort-of" what you need to do...
First step is to create a remote-able object that will be used to communicate with the domain. It's only needed method is one to return the output of a page:
internal class RemoteAspDomain : MarshalByRefObject
{
public string ProcessRequest(string page, string query)
{
using (StringWriter sw = new StringWriter())
{
SimpleWorkerRequest work = new SimpleWorkerRequest(page, query, sw);
HttpRuntime.ProcessRequest(work);
return sw.ToString();
}
}
}
Then when your ready to create/work with ASP.Net you setup the environment like:
public static string RunAspPage(string rootDirectory, string page, string query)
{
RemoteAspDomain host;
try
{
host = (RemoteAspDomain)ApplicationHost.CreateApplicationHost(typeof(RemoteAspDomain), "/", rootDirectory);
return host.ProcessRequest(page, query);
}
finally
{
ApplicationManager.GetApplicationManager().ShutdownAll();
System.Web.Hosting.HostingEnvironment.InitiateShutdown();
host = null;
}
}
Now you should be able to use it with the following:
string response = RunAspPage("C:\\MyWebAppRoot\\", "/default.aspx", "asdf=123&xyz=123");
Obviously, you don't want to do this for every request as it takes time to perform the startup-shutdown operations. Simply refactor the RunAspPage to be an IDisposable class that destroys the environment on dispose instead of using the finally block.
Update, BTW if Your already running in an ASP.Net session, there are far easier ways to do this. See HttpServerUtility.Execute Method (String, TextWriter)
Please Note: The above code was copy/pasted and simplified from a working copy, I think I got everything you need but my actual implementation is much more complicated. If you have any trouble there are several real-world examples of these API on the internet.
Is there a better way to generate HTML email in C# (for sending via System.Net.Mail), than using a Stringbuilder to do the following:
string userName = "John Doe";
StringBuilder mailBody = new StringBuilder();
mailBody.AppendFormat("<h1>Heading Here</h1>");
mailBody.AppendFormat("Dear {0}," userName);
mailBody.AppendFormat("<br />");
mailBody.AppendFormat("<p>First part of the email body goes here</p>");
and so on, and so forth?
You can use the MailDefinition class.
This is how you use it:
MailDefinition md = new MailDefinition();
md.From = "test#domain.example";
md.IsBodyHtml = true;
md.Subject = "Test of MailDefinition";
ListDictionary replacements = new ListDictionary();
replacements.Add("{name}", "Martin");
replacements.Add("{country}", "Denmark");
string body = "<div>Hello {name} You're from {country}.</div>";
MailMessage msg = md.CreateMailMessage("you#anywhere.example", replacements, body, new System.Web.UI.Control());
Also, I've written a blog post on how to generate HTML e-mail body in C# using templates using the MailDefinition class.
Use the System.Web.UI.HtmlTextWriter class.
StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);
html.RenderBeginTag(HtmlTextWriterTag.H1);
html.WriteEncodedText("Heading Here");
html.RenderEndTag();
html.WriteEncodedText(String.Format("Dear {0}", userName));
html.WriteBreak();
html.RenderBeginTag(HtmlTextWriterTag.P);
html.WriteEncodedText("First part of the email body goes here");
html.RenderEndTag();
html.Flush();
string htmlString = writer.ToString();
For extensive HTML that includes the creation of style attributes HtmlTextWriter is probably the best way to go. However it can be a bit clunky to use and some developers like the markup itself to be easily read but perversly HtmlTextWriter's choices with regard indentation is a bit wierd.
In this example you can also use XmlTextWriter quite effectively:-
writer = new StringWriter();
XmlTextWriter xml = new XmlTextWriter(writer);
xml.Formatting = Formatting.Indented;
xml.WriteElementString("h1", "Heading Here");
xml.WriteString(String.Format("Dear {0}", userName));
xml.WriteStartElement("br");
xml.WriteEndElement();
xml.WriteElementString("p", "First part of the email body goes here");
xml.Flush();
Updated Answer:
The documentation for SmtpClient, the class used in this answer, now reads, 'Obsolete("SmtpClient and its network of types are poorly designed, we strongly recommend you use https://github.com/jstedfast/MailKit and https://github.com/jstedfast/MimeKit instead")'.
Source: https://www.infoq.com/news/2017/04/MailKit-MimeKit-Official
Original Answer:
Using the MailDefinition class is the wrong approach. Yes, it's handy, but it's also primitive and depends on web UI controls--that doesn't make sense for something that is typically a server-side task.
The approach presented below is based on MSDN documentation and Qureshi's post on CodeProject.com.
NOTE: This example extracts the HTML file, images, and attachments from embedded resources, but using other alternatives to get streams for these elements are fine, e.g. hard-coded strings, local files, and so on.
Stream htmlStream = null;
Stream imageStream = null;
Stream fileStream = null;
try
{
// Create the message.
var from = new MailAddress(FROM_EMAIL, FROM_NAME);
var to = new MailAddress(TO_EMAIL, TO_NAME);
var msg = new MailMessage(from, to);
msg.Subject = SUBJECT;
msg.SubjectEncoding = Encoding.UTF8;
// Get the HTML from an embedded resource.
var assembly = Assembly.GetExecutingAssembly();
htmlStream = assembly.GetManifestResourceStream(HTML_RESOURCE_PATH);
// Perform replacements on the HTML file (if you're using it as a template).
var reader = new StreamReader(htmlStream);
var body = reader
.ReadToEnd()
.Replace("%TEMPLATE_TOKEN1%", TOKEN1_VALUE)
.Replace("%TEMPLATE_TOKEN2%", TOKEN2_VALUE); // and so on...
// Create an alternate view and add it to the email.
var altView = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
msg.AlternateViews.Add(altView);
// Get the image from an embedded resource. The <img> tag in the HTML is:
// <img src="pid:IMAGE.PNG">
imageStream = assembly.GetManifestResourceStream(IMAGE_RESOURCE_PATH);
var linkedImage = new LinkedResource(imageStream, "image/png");
linkedImage.ContentId = "IMAGE.PNG";
altView.LinkedResources.Add(linkedImage);
// Get the attachment from an embedded resource.
fileStream = assembly.GetManifestResourceStream(FILE_RESOURCE_PATH);
var file = new Attachment(fileStream, MediaTypeNames.Application.Pdf);
file.Name = "FILE.PDF";
msg.Attachments.Add(file);
// Send the email
var client = new SmtpClient(...);
client.Credentials = new NetworkCredential(...);
client.Send(msg);
}
finally
{
if (fileStream != null) fileStream.Dispose();
if (imageStream != null) imageStream.Dispose();
if (htmlStream != null) htmlStream.Dispose();
}
I use dotLiquid for exactly this task.
It takes a template, and fills special identifiers with the content of an anonymous object.
//define template
String templateSource = "<h1>{{Heading}}</h1>Dear {{UserName}},<br/><p>First part of the email body goes here");
Template bodyTemplate = Template.Parse(templateSource); // Parses and compiles the template source
//Create DTO for the renderer
var bodyDto = new {
Heading = "Heading Here",
UserName = userName
};
String bodyText = bodyTemplate.Render(Hash.FromAnonymousObject(bodyDto));
It also works with collections, see some online examples.
I would recomend using templates of some sort. There are various different ways to approach this but essentially hold a template of the Email some where (on disk, in a database etc) and simply insert the key data (IE: Recipients name etc) into the template.
This is far more flexible because it means you can alter the template as required without having to alter your code. In my experience your likely to get requests for changes to the templates from end users. If you want to go the whole hog you could include a template editor.
As an alternative to MailDefinition, have a look at RazorEngine https://github.com/Antaris/RazorEngine.
This looks like a better solution.
Attributted to...
how to send email wth email template c#
E.g
using RazorEngine;
using RazorEngine.Templating;
using System;
namespace RazorEngineTest
{
class Program
{
static void Main(string[] args)
{
string template =
#"<h1>Heading Here</h1>
Dear #Model.UserName,
<br />
<p>First part of the email body goes here</p>";
const string templateKey = "tpl";
// Better to compile once
Engine.Razor.AddTemplate(templateKey, template);
Engine.Razor.Compile(templateKey);
// Run is quicker than compile and run
string output = Engine.Razor.Run(
templateKey,
model: new
{
UserName = "Fred"
});
Console.WriteLine(output);
}
}
}
Which outputs...
<h1>Heading Here</h1>
Dear Fred,
<br />
<p>First part of the email body goes here</p>
Heading Here Dear Fred, First part of the email
body goes here
Emitting handbuilt html like this is probably the best way so long as the markup isn't too complicated. The stringbuilder only starts to pay you back in terms of efficiency after about three concatenations, so for really simple stuff string + string will do.
Other than that you can start to use the html controls (System.Web.UI.HtmlControls) and render them, that way you can sometimes inherit them and make your own clasess for complex conditional layout.
If you don't want a dependency on the full .NET Framework, there's also a library that makes your code look like:
string userName = "John Doe";
var mailBody = new HTML {
new H(1) {
"Heading Here"
},
new P {
string.Format("Dear {0},", userName),
new Br()
},
new P {
"First part of the email body goes here"
}
};
string htmlString = mailBody.Render();
It's open source, you can download it from http://sourceforge.net/projects/htmlplusplus/
Disclaimer: I'm the author of this library, it was written to solve the same issue exactly - send an HTML email from an application.
You might want to have a look at some of the template frameworks that are available at the moment. Some of them are spin offs as a result of MVC but that isn't required. Spark is a good one.
A commercial version which I use in production and allows for easy maintenance is LimiLabs Template Engine, been using it for 3+ years and allows me to make changes to the text template without having to update code (disclaimers, links etc..) - it could be as simple as
Contact templateData = ...;
string html = Template
.FromFile("template.txt")
.DataFrom(templateData )
.Render();
Worth taking a look at, like I did; after attempting various answers mentioned here.