views:

470

answers:

9

I need to send email notifications to users and I need to allow the admin to provide a template for the message body (and possibly headers, too).

I'd like something like string.Format that allows me to give named replacement strings, so the template can look like this:

Dear {User},

Your job finished at {FinishTime} and your file is available for download at {FileURL}.

Regards,

-- 
{Signature}

What's the simplest way for me to do that?

+8  A: 

Use a templating engine. StringTemplate is one of those, and there are many.

Anton Gogolev
+1. That's what I use for most scenarios.
RichardOD
A: 

If you need something very powerfull (but really not the simplest way) you can host Asp.Net and use it as your templating engine.

You'll have all the power of Asp.Net to format the body of your message.

Think Before Coding
+5  A: 

You could use string.Replace(...), eventually in a for-each through all the keywords. If there are only a few keywords you can have them on a line like this:

string myString = template.Replace("FirstName", "John").Replace("LastName", "Smith").Replace("FinishTime", DateTime.Now.ToShortDateString());

Or you could use Regex.Replace(...), if you need something a bit more powerful and with more options.

Read this article on codeproject to view which string replacement option is fastest for you.

Ovi
It's not good way, if you have a lot of parameters to replace and/or have long string - because string is immutable, so there is big memory usage.
TcKs
+1 @TcKs - agreed, the example has 4 parameters so I'd go with @Ovi's suggestion, but if it got to around 10 or so then I'd look to a templating language..
Andrew
This could use lots of memory, but a templating engine or language could very well be overkill also.
SnOrfus
The Garbage Collector will address some relatives of yours if you know what I mean...
Andrei Rinea
+1  A: 

You can use "string.Format" method:

var user = GetUser();
var finishTime = GetFinishTime();
var fileUrl = GetFileUrl();
var signature = GetSignature();
string msg = 
@"Dear {0},

Your job finished at {1} and your file is available for download at {2}.

Regards,

-- 
{3}";
msg = string.Format( msg, user, finishTime, fileUrl, signature );

It allows you to change content in future and si friendly for localization.

TcKs
That only allows numbered inputs, and throws an exception if the user puts in too high a number.
Simon
Yes only numbered inputs, ok.But "throws an exception if the user puts in too high a number" is not true. I tried it with "1024 * 1024" input parameters and it works.
TcKs
No, I mean if you replaced {3} with {4} but don't provide another input parameter.
Simon
+3  A: 

Actually, you can use XSLT. You create a simple XML template:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:template match="TETT">
    <p>
       Dear <xsl:variable name="USERNAME" select="XML_PATH" />,

       Your job finished at <xsl:variable name="FINISH_TIME" select="XML_PATH" /> and your file is available for download at <xsl:variable name="FILE_URL" select="XML_PATH" />.

       Regards,
        -- 
       <xsl:variable name="SIGNATURE" select="XML_PATH" />
    </p>
</xsl:template>

Then create a XmlDocument to perform transformation against: XmlDocument xmlDoc = new XmlDocument();

        XmlNode xmlNode = xmlDoc .CreateNode(XmlNodeType.Element, "EMAIL", null);
        XmlElement xmlElement= xmlDoc.CreateElement("USERNAME");
        xmlElement.InnerXml = username;
        xmlNode .AppendChild(xmlElement); ///repeat the same thing for all the required fields

        xmlDoc.AppendChild(xmlNode);

After that, apply the transformation:

        XPathNavigator xPathNavigator = xmlDocument.DocumentElement.CreateNavigator();
        StringBuilder sb = new StringBuilder();
        StringWriter sw = new StringWriter(sb);
        XmlTextWriter xmlWriter = new XmlTextWriter(sw);
        your_xslt_transformation.Transform(xPathNavigator, null, xmlWriter);
        return sb.ToString();
ifesdjeen
Much as I love XSLT, this probably fails the last statement in the OP: "What's the simplest way for me to do that?":)
well...you still can use string.format...
ifesdjeen
i was replying "What’s a good way of doing string templating in .NET?") not the "easiest")
ifesdjeen
"You create a *simple* XML template"??! What?! There is no such thing!!!
Vulcan Eager
A: 

There are some excellent suggestions in the answers to this question:

http://stackoverflow.com/questions/620265/

Josh Hinman
A: 

Implementing your own custom formatter might be a good idea.

Here's how you do it. First, create a type that defines the stuff you want to inject into your message. Note: I'm only going to illustrate this with the User part of your template...

class JobDetails
{
    public string User 
    { 
        get;
        set; 
    }        
}

Next, implement a simple custom formatter...

class ExampleFormatter : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        return this;
    }

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        // make this more robust
        JobDetails job = (JobDetails)arg;

        switch (format)
        {
            case "User":
            {
                return job.User;
            }
            default:
            {
                // this should be replaced with logic to cover the other formats you need
                return String.Empty;
            }
        }
    }
}

Finally, use it like this...

string template = "Dear {0:User}. Your job finished...";

JobDetails job = new JobDetails()
                     {
                             User = "Martin Peck"
                     };

string message = string.Format(new ExampleFormatter(), template, job);

... which will generate the text "Dear Martin Peck. Your job finished...".

Martin Peck
+1  A: 

There's some nice background on this, with an implementation, on Phil Haack's blog: http://haacked.com/archive/2009/01/14/named-formats-redux.aspx

Dave Cluderay
A: 

If you are coding in VB.NET you can use xml literals. If you are coding in C# you can use ShartDevelop to have files in VB.NET in the same project as C# code.

epitka