• Razor模板引擎


    Razor模板引擎

    一、简介

      在MVC以外的场景中,我们往往需要完成一些模板引擎生成代码或页面的工作;在以前我们一般常用的有Razor、NVeocity、VTemplate。虽然所有的模板系统都具有一些共同特征,但 Razor却和我们前面讨论的二种视图引擎截然不同。不同于其它视图引擎,Razor在使用XML代 码方面没有走得那么极端。它也不完全类似于ASPX,因为它把那些比较笨重的占位符替换成@符号接表达式或者普通的控制块。因为不需要特殊的结束标记,所 以Razor最终的代码很简练。

      本篇介绍的主角是Razor,在非Core的版本中,我们常用开源的RazorEngine来解决我们的问题;但是它却没有对应.NET Core的版本。我们也只要自己动手来完成一个支持.NET Core的“模板引擎”版本。

      一般情况下使用Razor作为视图引擎要实现如下步骤:

      (1)读取模板文件 -> (2)生成Raozr的C#代码 -> (3)使用Roslyn编译代码生成程序集 -> (4)动态加载程序集 -> (5)反射调用

      

    二、非Mvc中使用Razor

      我们一般在使用Razor时都是在ASP.NET MVC中使用.cshtml来作为模板,由ASP.NET MVC的视图引擎(ViewEngine)来生成页面的代码的,总之,这里想说的是,模板引擎是独立的,它们甚至是独立的项目,由不同的公司和组织来开发。这次我们要在非Mvc中使用Raozr;首先我们要“脱离”Mvc的环境。

    这里我们只在.NET Core程序中引用微软Raozr部分的程序集Microsoft.AspNetCore.Razor 1.0版本,这个程序集负责将模板生成出C#代码。

    1.Project.json添加引用

    1. "dependencies": {
    2. "Microsoft.AspNetCore.Razor": "1.0.0"
    3. "NETStandard.Library": "1.6.0"
    4. }

    2.模板生成代码

      如下是摘录的YOYOFx框架中的一段代码,因为我们要生成代码时一般需要传入Model数据,这时需要Model Type组织代码时,要将泛型的情况考虑进去 。这里的RazorViewTemplate是一个模板基类,这里包含了模板中调用的外部方法,我们常用到的如HtmlHelper、Render、Url、Raw等方法或类都是通过这个柜顶模板定义的,RazorViewTemplate是一个自定义类不需要继承其它类型,如果想扩展模板中使用的方法,只需要在这个类中加入即可。

    1. public class CodeGenerateService
    2. {
    3. public GeneratorResults Generate(Type modelType,string template)
    4. {
    5. //准备临时类名,读取模板文件和Razor代码生成器
    6. string templateType = "YOYO.AspNetCore.ViewEngine.Razor.RazorViewTemplate";
    7. string templateTypeName = modelType != null ? string.Format(templateType + @"<{0}>", modelType.FullName) : templateType;
    8. var class_name = "c" + Guid.NewGuid().ToString("N");
    9. var host = new RazorEngineHost(new CSharpRazorCodeLanguage(), () => new HtmlMarkupParser())
    10. {
    11. DefaultBaseClass = templateTypeName,
    12. DefaultClassName = class_name,
    13. DefaultNamespace = "YOYO.AspNetCore.ViewEngine.Razor",
    14. GeneratedClassContext =
    15. new GeneratedClassContext("Execute", "Write", "WriteLiteral", "WriteTo",
    16. "WriteLiteralTo",
    17. "RazorViewTemplate.Dynamic", new GeneratedTagHelperContext())
    18. };
    19. host.NamespaceImports.Add("System");
    20. host.NamespaceImports.Add("System.Dynamic");
    21. host.NamespaceImports.Add("System.Linq");
    22. host.NamespaceImports.Add("System.Collections.Generic");
    23. var engine = new RazorTemplateEngine(host);
    24. return engine.GenerateCode(new StringReader(template));
    25. }
    26. }

      通过以上代码得到GeneratorResults类型的结果,从而可以得知生成过程是否成功,错误在位置等信息。最后通过GeneratedCode属性,得到生成好的C#代码。

        3.编译模板

      一般Razor的C#代码生成后,都是通过CodeDom来生成和编译代码的;.NET开源后,我们又多了一个强大的选择Roslyn , Roslyn也是支持.NET Core的,并且在整个.NET平台中,扮演着非常重要的角色,小到这种视图代码编译,大到整个项目的编译场景都有Roslyn的身影。微软最新开源的Visual Studio Code中C#插件,OmniSharp就是通过Roslyn来对项目和编辑器提供支持的。

      摘录YOYOFx代码如下:

    1. public Type Compile(string compilationContent)
    2. {
    3. var assemblyName = Path.GetRandomFileName();
    4. var sourceText = SourceText.From(compilationContent, Encoding.UTF8);
    5. var syntaxTree = CSharpSyntaxTree.ParseText( sourceText, path: assemblyName, options: new CSharpParseOptions());
    6. var compilation = CSharpCompilation.Create(assemblyName, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
    7.     syntaxTrees: new[] { syntaxTree },
    8.     references: ApplicationReferences );
    9. using (var assemblyStream = new MemoryStream())
         {
    10. var result = compilation.Emit(assemblyStream, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb));
    11. this.CompileResult = new CompileResult(){ Success = result.Success, Errors = result.Diagnostics.Select(d => d.ToString()).ToList()};
    12. if (!result.Success){
    13. if (!compilation.References.Any() && !ApplicationReferences.Any())
    14. throw new InvalidOperationException("project.json preserveCompilationContext");
    15. return null;
    16. }
    17. var templateType = LoadTypeForAssemblyStream(assemblyStream, null);
    18. return templateType;
    19. }
    20. }

       在代码中可以通过CompileResult得到相应的编译错误信息,同样包括错误的信息和具体错误所在的行。

      其中注意的是LoadTypeForAssemblyStream方法,因为在.NET Core中动态加载程序集的方式跟以前有所不同AppDomain的概念现在已经消失,所以要在.NET Core动态加载程序集要使用,命名空间System.Runtime.Loader中的AssemblyLoadContext.Default.LoadFromStream 方法,而在.NET 4.5+中要使用Assembly.Load方法。

     三、总结

       Razor 不仅仅使用了动态的编译,还有一个强大的模板解析的功能。利用自定义的模板基类还可以在模板里提供一些辅助方法。这样看来 Razor 也算是 C# DSL 的一种实现了。

      GitHubhttps://github.com/maxzhang1985/YOYOFx  如果觉还可以请Star下, 欢迎一起交流。

  • 相关阅读:
    常用SEO优化工具
    OA系统中常用信息提示窗体
    VB.NET 操作注册表
    js截取字符串处理
    JavaScript中常用的对象和属性
    优化ASP.NET性能
    jquery线上引用无需本地包 Jim
    css 超出盒子滚动,不显示滚动条 Jim
    常用判断js数据类型 Jim
    amonthpicker 禁止当前完后月份,禁止当前往前推2月份 Jim
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5948995.html
Copyright © 2020-2023  润新知