自从 ASP.NET MVC 3 推出来之后,其中最大的亮点当数 MVC 3 里的 Razor 页面引擎。用 @{ } 取代了以前的 <%= %>,简洁的语法让开发者赞不绝口。
和 MVC 开源一样,Codeplex 上也开源了这个引擎:RazorEngine , 熟悉MVC开发的童鞋都知道这其中的奥秘,主要是使用了.NET 4.0 dynamic 动态对象。
然后 RazorEngine 会将 template 生成一个临时的 .cs 文件,然后编译并调用。
然后就可以在代码中动态获得结果了:
(msdn: 使用预处理 T4 文本模板生成运行时文本)
再来看看 RazorEngine :
接下来看看如何利用 Razor 打造一个泛用的代码生成器:
定义两个输入区域,一个是“全局数据”,一个是“列表数据”。全局数据输入单值即不循环的,列表数据是多行的用于循环输出。
模板页可以随时修改,保存。模板里的变量当然是根据前面的输入来定义的,总是要有规律的嘛。
其实对于javascript, ruby, python 这样的动态语言,实现上面的功能也不是什么难事。但 Razor 不仅仅提供了动态的编译,还有一个强大的模板解析的功能。
利用 @help 还可以在模板里提供一些辅助方法。这样看来 Razor 也算是 C# DSL 的一种实现了。
和 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 的一种实现了。