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下, 歡迎一起交流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM