I have a set of templates for emails that my app sends out. The templates have codes embedded in them that correspond to properties of my business object. Is there a more elegant way than calling string.Replace("{!MyProperty!}", item.MyProperty.ToString()) a zillion times? Maybe Xml Transform, regular expressions, or some other magic? I'm using C# 3.5
First of all, when I do this I use StringBuilder.Replace() as I have found that its performance is much better suited when working with 3 or more replacements.
Of course there are other ways of doing it, but I've found that it is not usually worth the extra effort to try other items.
You might be able to use Reflection I guess to automate the replacement, that might be the only "better" way.
You could do it using a regex, but your regex replace differs also for each property. I'd stick with the string.Replace.
Use reflection to retrieve the properties and replace it in a loop:
foreach (string property in properties)
{
string.Replace("{!"+property+"!}",ReflectionHelper.GetStringValue(item,property));
}
Just implement your ReflectionHelper.GetStringValue method and use reflection to retrieve all the properties on your item object type.
public class Template {
static Regex TAG_RE = new Regex(@"\{!(\w+)!}");
public Template(object item) {
this.item = item;
}
private string resolver(Match match) {
string tag = match.Group[1];
if (tag == "MyProperty") {
return item.MyProperty.toString();
}
return "";
}
public string translate(string text) {
return TAG_RE.Replace(text, new MatchEvaluator(this.resolver));
}
}
(There could be some syntax and/or semantic errors)
There's a built in WebControl, System.Web.UI.WebControls.MailDefinition that does string replacements (among other things). Pity they tightly coupled it to the Smtp settings in app.config and a web control, and then made it sealed to foil inheritors.
But, it does handle a few things you'd most likely want in a mail template engine - body text from a file, html email, embedded objects, etc. Reflector shows the actual replacement is handled with a foreach loop and Regex.Replace - which seems a reasonable choice to me as well.
A quick glance through shows that if you can live with the from address being in the app.config (you can change it on the returned MailMessage afterwards), you only need the owner control for embedded resources or the BodyFileName.
If you're using ASP.NET or can live with the limitations - I'd choose MailDefinition. Otherwise, just do a foreach over a dictionary and a Regex.Replace. It's a little memory hungry, because of the repeated allocations of body - but they're short lived and shouldn't pose much of a problem.
var replacements = new Dictionary<string, object>() {
{ "Property1", obj.Property1 },
{ "Property2", obj.Property2 },
{ "Property3", obj.Property3 },
{ "Property4", obj.Property4 },
}
foreach (KeyValuePair<string, object> kvp in replacement) {
body = Regex.Replace(body, kvp.Key, kvp.Value.ToString());
}
If you really have a lot of properties, then read your body first with Regex.Match and reflect to the properties instead.