Note: The entry originally appeared on the tech blog at collectedit.com on 08/20/2012
One of the first large-ish projects I worked on at CollectedIt was creating a notification system. This would notify users of actions that other users took with their collection (commented, agree/disagree with item stats, etc). While I hope to blog more about the all the technical challenges that came about developing the system today I am going to concentrate on some of the text templating we do with the notifications.

CollectedIt is architected in such a way that database calls are few and far between whenever the current action is on a code path that could be called from a user facing interface (website, iphone app, etc). This architecture minimizes lag time and leaves us in a good position to scale out. However, as with most forms of architecture, there are trade offs. The biggest trade off for notification system was that the front-end knew enough about the action that was being performed that should generate a notification, but knew virtually no details about the objects that were part of the notifications.
A more concrete example:
Arthur is browsing medieval collections and stumbles upon an item in Tim’s “Enchanted Weapons” collection called “Staff that shoots flames”. This item is really neat to Arthur so he wishes to give Tim a kudos. Once Arthur clicks the kudos button this triggers a notification. At notification generation time all that is known is:
- Logged in user id
- Current collection id
- Current item id
- A kudos was given
Nothing is known about Arthur, or Tim, or “Enchanted Weapons” or “Staff that shoots flames”. It would be fairly trivial to the perform a DB query joining together 3 or 4 tables to get all the information needed, but we are in code that is executed as result of the kudos button being clicked so we want to get back to the user as soon as possible. Doing an extra DB query (particularly one that involved 4 tables) is not the quickest way to get that information.
What was decided was that the notification could be generated with tokens that could be replaced later down the line. The first thought was to just use Razor which would be cool however the Razor Parser is marked for internal use and I have been burnt by using internal methods before (not to say it’s never appropriate to use undocumented methods…but that’s another blog entry). Back to GoogleDuckDuckGo to see if anything is out there for me to do some sort of text templating with the CollectedIt object and some text.
I ran into T4 which at first look seem like it would work. Looking deeper though there is a compile time class that gets generated and the runtime just uses that generated object to do the processing. This won’t really work since the template is also generated at runtime.
A little more time searching I came up with nothing that would really do what I wanted. So I decided to experiment a little writing my own. Since I wanted to write this quick and there really is no reason to write a full blown text processor (although that would be fun) I needed to boil down what exactly it was I was trying to accomplish.
- Flexible text replacement
- Not much logic necessarily needed inside the template itself
- Both template and replacement objects would be generated at runtime
First thing I decided to do was take a look at what I could get with C#’s dynamic type. I have used dynamic objects in the past to do things like Object.RuntimeProperty but that’s not exactly what I have here. I have Object and “RuntimeProperty” where “RuntimeProperty” is just a string. There may well be a way to use “RuntimeProperty” directly on a dynamic object, but I could not find one (if anybody knows of a way let me know in the comments). Instead I went down the reflection route since at runtime there is really nothing different between a dynamic object and a compiled object when inspecting objects with reflection.
Type dynamicType = o.GetType();
PropertyInfo p = dynamicType.GetProperty(property);
object dynamicPropValue = p.GetValue(o, null);
FieldInfo f = dynamicType.GetField(property);
object dynamicFieldValue = f.GetValue(o);
Great! That takes care of runtime objects and their properties. What about the text template itself though. Well…I know regular expressions.

In order to not completely reinvent the wheel I picked the T4 syntax (and specifically only the subset of T4 that replaces the text template with a string: <#= Property #>). This is pretty easy to detect with a regex:
<#=\s*(?[a-zA-Z]\w*)\s*#>
With the reflection and the regex it gives just us all the tools that are need to satisfy the requirements we came up with. All that’s left is to package it up in a nice usable package. In order to figure out exactly how to package it up I looked at how exactly the text templating would be called.
Continuing with the Arthur/Tim example from above the code creating the kudos notification would like to generate the notification with an interface like
string notificationText =
"<#= Author > really likes your <#= Item #> in <#= Collection >";
string notification = template.ProcessTokens(new {
Author = "Arthur",
Item = Staff that shoots flames",
Collection = "Enchanted Weapons"
});
This points to using an extension method. In fact that is exactly what we went with. The whole extension method is
public static string ProcessTokens(this string s, dynamic o)
{
Type dynamicType = o.GetType();
string composedString = s;
MatchCollection tokens = _tokenRegex.Matches(s);
foreach (Match token in tokens)
{
string property = token.Groups["prop"].Value;
PropertyInfo p = dynamicType.GetProperty(property);
if (p != null)
composedString = composedString.Replace(token.ToString(), String.Format("{0}", p.GetValue(o, null)));
else
{
FieldInfo f = dynamicType.GetField(property);
if (f != null)
composedString = composedString.Replace(token.ToString(), String.Format("{0}", f.GetValue(o)));
}
}
return composedString;
}
private static readonly Regex _tokenRegex = new Regex(@"<#=\s*(?<prop>[a-zA-Z]\w*)\s*#>");
That’s how we solved the problem of having a disjointed read/write object system. Feel free to use the code snippets above in your own projects to solve any sort of problem where you need runtime text and runtime objects to generate a string. Also make sure to drop by collectedit.com with questions, suggestions, or just some kudos.