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");
Related
I decided to get into coding and am learning c#, after making a few small projects, I decided to step it up a little and make a text adventure game, with saving and loading, and if I get to feeling zany I'll try to add some multiplayer. While I haven't really hit a road block because of it, I can't help but feel that I am doing load function REALLY sub-optimally. The save is fine, I feel like it works for me, but the load I feel can be really simplified, I just don't know what to use.
I also wouldn't really mind, but with this way, if I add other attributes/skills or whatever else that needs to be saved, I will have to add everything to the load function as well, and it will be even longer.
I have tried to search around on here, the c# documentation, and other sites, but can't find a solution that works for this case. can anyone help me find a better way of doing this? Or is this the best I can really do since it's varying data types?
Edit: To simplify and clarify what answer I am searching for, I am trying to find a simpler and more scalable way to save and load the data to a file.
static void LoadGame(CharData PlayerData)
{
Console.WriteLine("Enter the name of the character to load as shown below.");
//getting current directory info, setting to di
DirectoryInfo di = new DirectoryInfo(Directory.GetCurrentDirectory());
//need to initialize these outside of a loop
int SaveFiles = 0;
string DisplayName = " ";
int DisplayNameLength = 0;
//looks through files in working directory ending in '.fasv', displays them in format '{x}. John Smith'
foreach (var fi in di.GetFiles("*.fasv"))
{
SaveFiles++;
DisplayNameLength = fi.Name.Length;
//remove .fasv from displayed name to make it look nicer
DisplayName = fi.Name.Remove(DisplayNameLength - 5, 5);
Console.WriteLine(SaveFiles.ToString() + ". " + DisplayName);
}
string toLoad = Console.ReadLine();
using StreamReader sr = new StreamReader(toLoad + ".fasv");
//the name is easy to get since it's a string. but integers...
PlayerData.Name = sr.ReadLine();
//... not so much. i hate all of this and i feel like it's gross, but i don't know how else to do it
int hp, xp, level, toughness, innovation, mind, empathy, spryness;
Int32.TryParse(sr.ReadLine(), out hp);
Int32.TryParse(sr.ReadLine(), out xp);
Int32.TryParse(sr.ReadLine(), out level);
Int32.TryParse(sr.ReadLine(), out toughness);
Int32.TryParse(sr.ReadLine(), out innovation);
Int32.TryParse(sr.ReadLine(), out mind);
Int32.TryParse(sr.ReadLine(), out empathy);
Int32.TryParse(sr.ReadLine(), out spryness);
PlayerData.Health = hp;
PlayerData.Level = level;
PlayerData.XP = xp;
PlayerData.Toughness = toughness;
PlayerData.Innovation = innovation;
PlayerData.Mind = mind;
PlayerData.Empathy = empathy;
PlayerData.Spryness = spryness;
sr.Close();
InGame(PlayerData);
}
static void SaveGame(CharData PlayerData)
{
using (StreamWriter sw = new StreamWriter(PlayerData.Name + ".fasv"))
{
foreach (System.Reflection.PropertyInfo stat in PlayerData.GetType().GetProperties())
{
//write player data properties to file line by line, using stat to iterate through the player data properties
sw.WriteLine(stat.GetValue(PlayerData));
}
sw.Close();
}
}
If you aren't set on a particular data format for the file data, I would recommend using a serializer such as JSON.NET. You can use NuGet to add newtonsoft.json to your project, and that would allow you to just do something similar to:
using (StreamWriter file = File.CreateText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
serializer.Serialize(file, playerData);
}
And then your code to read from the file would be pretty similar:
using (var file = File.OpenText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
return (CharData)serializer.Deserialize(file, typeof(CharData));
}
I borrowed those code snippets from newtonsoft.com. CreateText will create (or overwrite) the file and write the object as a JSON object.
I have the following code which tries to read data from a text file (so users can modify easily) and auto format a paragraph based on a the words in the text document plus variables in the form. I have the file "body" going into a field. my body text file has the following data in it
"contents: " + contents
I was hoping based on that to get
contents: Item 1, 2, etc.
based on my input. I only get exactly whats in the text doc despite putting "". What am I doing wrong? I was hoping to get variables in addition to my text.
string readSettings(string name)
{
string path = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/Yuneec_Repair_Inv";
try
{
// Create an instance of StreamReader to read from a file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(path + "/" + name + ".txt"))
{
string data = sr.ReadToEnd();
return data;
}
}
catch (Exception e)
{
// Let the user know what went wrong.
Console.WriteLine("The settings file for " + name + " could not be read:");
Console.WriteLine(e.Message);
string content = "error";
return content;
}
}
private void Form1_Load(object sender, EventArgs e)
{
createSettings("Email");
createSettings("Subject");
createSettings("Body");
yuneecEmail = readSettings("Email");
subject = readSettings("Subject");
body = readSettings("Body");
}
private void button2_Click(object sender, EventArgs e)
{
bodyTextBox.Text = body;
}
If you want to provide the ability for your users to customize certain parts of the text you should use some "indicator" that you know before hand, that can be searched and parsed out, something like everything in between # and # is something you will read as a string.
Hello #Mr Douglas#,
Today is #DayOfTheWeek#.....
At that point your user can replace whatever they need in between the # and # symbols and you read that (for example using Regular Expressions) and use that as your "variable" text.
Let me know if this is what you are after and I can provide some C# code as an example.
Ok, this is the example code for that:
StreamReader sr = new StreamReader(#"C:\temp\settings.txt");
var set = sr.ReadToEnd();
var settings = new Regex(#"(?<=\[)(.*?)(?=\])").Matches(set);
foreach (var setting in settings)
{
Console.WriteLine("Parameter read from settings file is " + setting);
}
Console.WriteLine("Press any key to finish program...");
Console.ReadKey();
And this is the source of the text file:
Hello [MrReceiver],
This is [User] from [Company] something else, not very versatile using this as an example :)
[Signature]
Hope this helps!
When you read text from a file as a string, you get a string of text, nothing more.
There's no part of the system which assumes it's C#, parses, compiles and executes it in the current scope, casts the result to text and gives you the result of that.
That would be mostly not what people want, and would be a big security risk - the last thing you want is to execute arbitrary code from outside your program with no checks.
If you need a templating engine, you need to build one - e.g. read in the string, process the string looking for keywords, e.g. %content%, then add the data in where they are - or find a template processing library and integrate it.
I'm using the latest version of razor from.
https://github.com/Antaris/RazorEngine
I want to attached it to some cshtml and debug against it.
The readme states the following
Debugging
One thing you might want to enable is the debugging feature:
config.Debug = true;
When Debug is true you can straight up debug into the generated code. RazorEngine also supports debugging directly into the
template files (normally .cshtml files). As as you might see in the
above code there is no file to debug into. To provide RazorEngine with
the necessary information you need to tell where the file can be
found:
string template = "Hello #Model.Name, welcome to RazorEngine!";
string templateFile = "C:/mytemplate.cshtml"
var result = Engine.Razor.RunCompile(new LoadedTemplateSource(template, templateFile), "templateKey", null, new {
In my code i have setup the following
var config = new TemplateServiceConfiguration();
// .. configure your instance
config.Debug = true;
var service = RazorEngineService.Create(config);
Engine.Razor = service;
//string template = "Hello #Model.Name, welcome to RazorEngine!";
string templateFile = "C:/mytemplate.cshtml";
var template = new LoadedTemplateSource("", templateFile);
var result = Engine.Razor.RunCompile(template, this.Name, null, model);
Now I have created a cshtml file at that path with the following in it.
#{
var a = 1;
a = a + a;
#a
}
<div>
hi
</div>
But I get returned an empty string :(
And when i f11 into it it just steps over :( :(.
Im not sure what im doing wrong anyone got any ideas.
Answer code
string templateFile = "C:/mytemplate.cshtml";
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(templateFile))
{
String line;
// Read and display lines from the file until the end of
// the file is reached.
while ((line = sr.ReadLine()) != null)
{
sb.AppendLine(line);
}
}
string allines = sb.ToString();
var template = new LoadedTemplateSource(allines, templateFile);
var result = Engine.Razor.RunCompile(template, this.Name, null, model);
The LoadedTemplateSource represents the template source code, and you have given "" as the source code, therefore your template is empty.
The first parameter of LoadedTemplateSource needs to be the source code of the template and the second one is the path to the file, which is only used for debugging purposes.
If you need lazy loading or a custom loader strategy you can either implement a custom ITemplateSource or ITemplateManager, however having the source available in memory all time improves some error messages as well.
matthid, a RazorEngine Contributor
Disabling "Enable Just My Code" in Options > Debugging > General worked for me.
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.
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.