Useful Runtime Text Templating

On numerous occations I’ve had to do some runtime creation of C# code. In the early days, I was tempted to directly write each source line to a file.

But quickly discovering, that I needed a solution where I could use template based generation of texts (source code). Where I can easily change certain parts of the text, but maintain the overall skeleton.

For this I’ve been using the following class quite often:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

namespace TriGemini.Templating
{
    /// 
<summary>
    /// TextTemplate Class
    /// Written By Henrik Brinch (TriGemini)
    /// www.trigemini.dk / www.henrikbrinch.dk
    /// Check out www.linkedin.com/in/trigemini
    /// 
    /// License terms:  Feel free to use the code in your own projects, however if distributing
    /// the source code, you must preserve this heading.
    /// 
    /// Please do not post this code on other sites, but link to this blog-post instead.
    /// If you really find it usefull and use it in commercial application, I'd like to hear about
    /// it - and you're very welcome to mention me your application credits 🙂
    /// </summary>

    public class TextTemplate
    {
        #region Private Fields
        private static readonly Regex _regex = new Regex(@"###(?<TAG>\w+)###", RegexOptions.Compiled);
        private readonly Dictionary<string, string> _tags = new Dictionary<string, string>();
        #endregion

        #region Public Properties
        public string TemplateContent { get; private set; }

        public string this[string tagName]
        {
            get
            {
                string result;
                _tags.TryGetValue(tagName, out result);

                return result;
            }
            set
            {
                if (!_tags.ContainsKey(tagName))
                {
                    _tags.Add(tagName, value);
                }
                else
                {
                    _tags[tagName] = value;
                }
            }
        }

        #endregion

        #region Public Methods
        public static TextTemplate FromEmbeddedResource(string name)
        {
            TextTemplate result;
            var asm = Assembly.GetExecutingAssembly();

            using (var stream = asm.GetManifestResourceStream(name))
            {
                result = FromStream(stream);
            }
                
            return result;
        }

        public static TextTemplate FromStream(Stream stream)
        {
            var result = new TextTemplate();

            using (var reader = new StreamReader(stream))
            {
                result.TemplateContent = reader.ReadToEnd();
            }

            return result;
        }


        public void ToStream(Stream stream)
        {
            using (var writer = new StreamWriter(stream))
            {
                writer.Write(ToString());
            }
        }

        public void ToStream(string filename)
        {
            using (var writer = new StreamWriter(filename))
            {
                writer.Write(ToString());
            }
        }

        public override string ToString()
        {
            return _regex.Replace(TemplateContent, match =>
                {
                    var tagName = match.Groups["TAG"].Value;
                    return this[tagName] ?? String.Empty;
                });
        }
        #endregion
    }
}

Example usage

I’ve chosen the characters “###” as enclosing mark for a tag (defined by the regular expression in the TextTemplate class).  So e.g. ###NAME### acts as a position within the template, that needs to be replaced with the given tag content.   Take the very simple text file:

Hello ###NAME### how are you today?

I can choose to place this template e.g. as an embedded text resource in my project or enclose it as file. To use it, I can do the following code:

            var temp = TextTemplate.FromEmbeddedResource("MyNamespace.TestFile.txt");
            temp["NAME"] = "Henrik";
            temp.ToStream(@"c:\temp\output.txt");