和 MVC 開源一樣,Codeplex 上也開源了這個引擎:RazorEngine , 熟悉MVC開發的童鞋都知道這其中的奧祕,主要是使用了.NET 4.0 dynamic 動態對象。
然後 RazorEngine 會將 template 生成一個臨時的 .cs 文件,然後編譯並調用。
說到代碼生成,大家可能都會想到 T4,可惜的是 T4 需要預編譯。
那麼就先來看看 t4 如何運行時獲得結果的代碼:
首先添加一個 HelloWorld.tt 內容如下:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ parameter name="name" type="System.String" #>
Hello <#= name#> Welcome to t4!
修改 tt 的 Customer Tool 屬性:TextTemplatingFileGenerator 改爲 TextTemplatingFilePreprocessor 保存之後,會自動生成一個 HelloWorld.cs 文件。然後就可以在代碼中動態獲得結果了:
HelloWorld t4Template = new HelloWorld();
t4Template.Session = new Dictionary<string, object> { { "name", "World" } };
t4Template.Initialize();
string result = t4Template.TransformText();
不難看出,T4 的模板需要預編譯,生成模板的靜態類型再動態傳入值生成結果。所以難以達到動態添加,修改模板的要求。(msdn:使用預處理 T4 文本模板生成運行時文本)
再來看看 RazorEngine :
string template = "Hello @Model.Name! Welcome to Razor!";
string result = Razor.Parse(template, new { Name = "World" });
沒有預編譯,只需要動態的傳值,字符串即模板。這就給模板的變化帶來極大的想象空間,比如:在線的模板修改保存,提供數據並生成結果。接下來看看如何利用 Razor 打造一個泛用的代碼生成器:
定義兩個輸入區域,一個是“全局數據”,一個是“列表數據”。全局數據輸入單值即不循環的,列表數據是多行的用於循環輸出。
模板頁可以隨時修改,保存。模板裏的變量當然是根據前面的輸入來定義的,總是要有規律的嘛。
輸出的效果
這樣任何項目,只要隨時定義模板就可即時獲得結果,也不要編譯。
其實實現也很簡單:輸入的全局數據和多行數據(第一行數據作爲 dynamic 對象的屬性)解析後填充到 ExpandoObject 裏交給 Razor 就可以了~
private List<dynamic> ParseListData(string content, char[] separator)
{
var datas = new List<dynamic>();
var lines = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length <= 0)
return datas;
string[] columns = null;
int index = 0;
foreach (var line in lines)
{
var handledline = line;
if (separator.Any(c => c == ' '))
{
handledline = Regex.Replace(line, "\\s+", " ");
}
if (index == 0)
{
columns = handledline.Split(separator, StringSplitOptions.RemoveEmptyEntries);
columns = columns.Select(col => col.Trim()).ToArray();
}
else
{
var fieldValues = handledline.Split(separator);
dynamic obj = new ExpandoObject();
var dict = (IDictionary<string, object>)obj;
for (int i = 0; i < fieldValues.Length; i++)
{
dict[columns[i].Trim()] = fieldValues[i].Trim();
}
datas.Add(obj);
}
index++;
}
return datas;
}
其實對於javascript, ruby, python 這樣的動態語言,實現上面的功能也不是什麼難事。但 Razor 不僅僅提供了動態的編譯,還有一個強大的模板解析的功能。
利用 @help 還可以在模板裏提供一些輔助方法。這樣看來 Razor 也算是 C# DSL 的一種實現了。