Try fast search NHibernate

13 August 2011

Parse string as Razor template

For a special requirement at work (actually a mix with self requirement) I need to get a chunk of HTML content from a persistence-system. Taken as is, it does not appear a big challenge but analyzing the problem a little bit more deeper I saw that that the HTML chunk may contain variables, it may need a model… to be short it may be so complex as a MVC-PartialView.

To achieve the target quickly I tried to find something in the NET but without so much lucky. I have found a framework that seems to do the work @razorengine but, after a quick look at the code, I didn’t feel so enthusiast especially for the use we have to give to this new feature of our application (web application in Azure with something high traffic). hmm…. so ? what I can do ?

Personally I think that the MVC team in Microsoft is one of the best teams Microsoft ever had and I’m pretty sure there should be a way to parse a string using Razor without so much static classes… here we go with the Saturday proof of concept.

The code will talk by itself…

The VisualStudio solution

StringAsRazorSolution

The base class

The beating heart of Razor is inside System.Web.Razor but to use it you need a class without any special reference, just some “special” methods.

namespace YourCompany.DynamicContents
{
    public abstract class DynamicContentGeneratorBase
    {
        private StringBuilder buffer;
        protected DynamicContentGeneratorBase()
        {
            DynModel = new ExpandoObject();
        }

        /// <summary>
        /// This is just a custom property
        /// </summary>
        public dynamic DynModel { get; set; }

        /// <summary>
        /// This method is required and have to be exactly as declared here.
        /// </summary>
        public abstract void Execute();

        /// <summary>
        /// This method is required and can be public but have to have exactly the same signature
        /// </summary>
        protected void Write(object value)
        {
            WriteLiteral(value);
        }

        /// <summary>
        /// This method is required and can be public but have to have exactly the same signature
        /// </summary>
        protected void WriteLiteral(object value)
        {
            buffer.Append(value);
        }

        /// <summary>
        /// This method is just to have the rendered content without call Execute.
        /// </summary>
        /// <returns>The rendered content.</returns>
        public string GetContent()
        {
            buffer = new StringBuilder(1024);
            Execute();
            return buffer.ToString();
        }
    }
}

Surprised ? yes!!… that is all you need.

The concept

With the System.Web.Razor.RazorTemplateEngine you can generate the source-code of concrete classes inherited from your abstract DynamicContentGeneratorBase. The generated code will have the concrete implementation of the abstract method Execute with the calls to the methods Write and WriteLiteral as required by your string. In practice giving a simple string as "<b>Well done @DynModel.Who !!</b>" the implementation of Execute, generated by Razor, will look like:

public override void Execute()
{
    WriteLiteral("<b>Well done ");
    Write(DynModel.Who);
    WriteLiteral(" !!</b>");
}

To use the generated source you have to compile it in an assembly and create an instance of the concrete class.

Usage

  1. static void Main(string[] args)
  2. {
  3.     const string dynamicallyGeneratedClassName = "DynamicContentTemplate";
  4.     const string namespaceForDynamicClasses = "YourCompany";
  5.     const string dynamicClassFullName = namespaceForDynamicClasses + "." + dynamicallyGeneratedClassName;
  6.  
  7.     var language = new CSharpRazorCodeLanguage();
  8.     var host = new RazorEngineHost(language)
  9.                   {
  10.                                 DefaultBaseClass = typeof(DynamicContentGeneratorBase).FullName,
  11.                                 DefaultClassName = dynamicallyGeneratedClassName,
  12.                                 DefaultNamespace = namespaceForDynamicClasses,
  13.                   };
  14.     host.NamespaceImports.Add("System");
  15.     host.NamespaceImports.Add("System.Dynamic");
  16.     host.NamespaceImports.Add("System.Text");
  17.     var engine = new RazorTemplateEngine(host);
  18.         
  19.     var tr = new StringReader(GetStringFromSomewhere()); // here is where the string come in place
  20.     GeneratorResults razorTemplate = engine.GenerateCode(tr);
  21.  
  22.     var compiledAssembly = CreateCompiledAssemblyFor(razorTemplate.GeneratedCode);
  23.  
  24.     var templateInstance = (DynamicContentGeneratorBase)compiledAssembly.CreateInstance(dynamicClassFullName);
  25.  
  26.     templateInstance.DynModel.Who = "Fabio";
  27.         
  28.     Console.WriteLine(templateInstance.GetContent());
  29.     Console.ReadLine();
  30. }
  31.  
  32. private static Assembly CreateCompiledAssemblyFor(CodeCompileUnit unitToCompile)
  33. {
  34.     var compilerParameters = new CompilerParameters();
  35.     compilerParameters.ReferencedAssemblies.Add("System.dll");
  36.     compilerParameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
  37.     compilerParameters.ReferencedAssemblies.Add("System.Core.dll");
  38.     compilerParameters.ReferencedAssemblies.Add(typeof(DynamicContentGeneratorBase).Assembly.Location);
  39.     compilerParameters.GenerateInMemory = true;
  40.  
  41.     CompilerResults compilerResults = new CSharpCodeProvider().CompileAssemblyFromDom(compilerParameters, unitToCompile);
  42.     Assembly compiledAssembly = compilerResults.CompiledAssembly;
  43.     return compiledAssembly;
  44. }
  45.  
  46. private static string GetStringFromSomewhere()
  47. {
  48.     return "<b>Well done @DynModel.Who !!</b>";
  49. }

Form line 7 to 20 I’m generating the source of a concrete class called YourCompany.DynamicContentTemplate using the Razor template engine, then I’m compiling the source in an in-memory assembly, at line 24 I’m creating an instance of the YourCompany.DynamicContentTemplate, at line 25 I’m assigning a value to my DynamicModel and the result is:

StringAsRazorResult

Winking smile

06 August 2011

Azure storage initialization

This is the “self response” to the previous post.

When your role starts on Azure there are some tasks which runs synchronously and some tasks which runs asynchronously. For complex startups you can set the configuration of each custom tasks in your ServiceDefinition.csdef. If you don’t need special tasks to setup your VM you will end initializing your Azure-storage (blob containers, tables and queues) somewhere. Our problem was exactly: WHERE?

If you have a WebRole you have two places where you can initialize the storage:

  • the WebRole
  • the Application_Start of the HttpApplication (Global.asax.cs)

Which is the right one ? are they ambivalent ? If you read the documentation there is something that is clear to me :

The Application_Start method is called after the RoleEntryPoint.OnStart method finishes. The Application_End method is called before the RoleEntryPoint.OnStop method is called.

From the doc the right place is the OnStart.

If you use the  AppSetting to store your account information (as we do to switch between production and test account) you have something like this (or an equivalent):

 

public static class AzureAccount
{
    private static string dataconnectionstring = "AzureDataConnectionString";

    static AzureAccount()
    {
        CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
        {
            string value = ConfigurationManager.AppSettings[configName];
            if (RoleEnvironment.IsAvailable)
            {
                value = RoleEnvironment.GetConfigurationSettingValue(configName);
            }
            
            configSetter(value);
        });
    }

    public static CloudStorageAccount DefaultAccount()
    {
        return CloudStorageAccount.FromConfigurationSetting(dataconnectionstring);
    }
}

and the WebRole overriding the OnStart should be safe:

public class WebRole : RoleEntryPoint
{
    public override bool OnStart()
    {
        InitializeAzureStorage();

        return base.OnStart();
    }

and the initialization with something like this:

CloudStorageAccount account = AzureAccount.DefaultAccount();

// initialize the storage using the account

but it does not work always and if you look around the NET you will see various posts using Thread.Sleep here and there. The fact is that if you are using just the Web.Config, to store the AppSetting needed by the storage initialization, you don’t know exactly when it will be available ergo those Thread.Sleep may work sometimes but not always. To be sure that the AppSetting will be the correct, and overall you actually have the AzureDataConnectionString, you have to modify both ServiceDefinition.csdef and ServiceConfiguration.cscfg in this way:

ServiceDefinition

ServiceConfiguration

Note: you don’t have to remove the AzureDataConnectionString from your web.config… it is still useful to run the application as a web-application.