.NET Core中使用Razor模板引擎

1、簡介

  在MVC之外的場景中,咱們每每須要完成一些模板引擎生成代碼或頁面的工做;在之前咱們通常經常使用的有RazorNVeocityVTemplate。雖然全部的模板系統都具備一些共同特徵,但 Razor卻和咱們前面討論的二種視圖引擎大相徑庭。不一樣於其它視圖引擎,Razor在使用XML代 碼方面沒有走得那麼極端。它也不徹底相似於ASPX,由於它把那些比較笨重的佔位符替換成@符號接表達式或者普通的控制塊。由於不須要特殊的結束標記,所 以Razor最終的代碼很簡練。html

  本篇介紹的主角是Razor,在非Core的版本中,咱們經常使用開源的RazorEngine來解決咱們的問題;可是它卻沒有對應.NET Core的版本。咱們也只要本身動手來完成一個支持.NET Core的「模板引擎」版本。git

  通常狀況下使用Razor做爲視圖引擎要實現以下步驟:github

  (1)讀取模板文件 -> (2)生成Raozr的C#代碼 -> (3)使用Roslyn編譯代碼生成程序集 -> (4)動態加載程序集 -> (5)反射調用json

  

2、非Mvc中使用Razor

  咱們通常在使用Razor時都是在ASP.NET MVC中使用.cshtml來做爲模板,由ASP.NET MVC的視圖引擎(ViewEngine)來生成頁面的代碼的,總之,這裏想說的是,模板引擎是獨立的,它們甚至是獨立的項目,由不一樣的公司和組織來開發。此次咱們要在非Mvc中使用Raozr;首先咱們要「脫離」Mvc的環境。框架

這裏咱們只在.NET Core程序中引用微軟Raozr部分的程序集Microsoft.AspNetCore.Razor 1.0版本,這個程序集負責將模板生成出C#代碼。dom

1.Project.json添加引用

 "dependencies": {
    "Microsoft.AspNetCore.Razor": "1.0.0"
    "NETStandard.Library": "1.6.0"
   }

2.模板生成代碼

  以下是摘錄的YOYOFx框架中的一段代碼,由於咱們要生成代碼時通常須要傳入Model數據,這時須要Model Type組織代碼時,要將泛型的狀況考慮進去 。這裏的RazorViewTemplate是一個模板基類,這裏包含了模板中調用的外部方法,咱們經常使用到的如HtmlHelper、Render、Url、Raw等方法或類都是經過這個櫃頂模板定義的,RazorViewTemplate是一個自定義類不須要繼承其它類型,若是想擴展模板中使用的方法,只須要在這個類中加入便可。編輯器

    public class CodeGenerateService
    {
        public GeneratorResults Generate(Type modelType,string template)
        {
            //準備臨時類名,讀取模板文件和Razor代碼生成器
            string templateType = "YOYO.AspNetCore.ViewEngine.Razor.RazorViewTemplate";
            string templateTypeName = modelType != null ? string.Format(templateType + @"<{0}>", modelType.FullName) : templateType;
            var class_name = "c" + Guid.NewGuid().ToString("N");
            var host = new RazorEngineHost(new CSharpRazorCodeLanguage(), () => new HtmlMarkupParser())
            {
                DefaultBaseClass = templateTypeName,
                DefaultClassName = class_name,
                DefaultNamespace = "YOYO.AspNetCore.ViewEngine.Razor",
                GeneratedClassContext =
                                   new GeneratedClassContext("Execute", "Write", "WriteLiteral", "WriteTo",
                                                             "WriteLiteralTo",
                                                             "RazorViewTemplate.Dynamic", new GeneratedTagHelperContext())
            };
            host.NamespaceImports.Add("System");
            host.NamespaceImports.Add("System.Dynamic");
            host.NamespaceImports.Add("System.Linq");
            host.NamespaceImports.Add("System.Collections.Generic");
            var engine = new RazorTemplateEngine(host);
            return engine.GenerateCode(new StringReader(template)); 
        }
   }

  經過以上代碼獲得GeneratorResults類型的結果,從而能夠得知生成過程是否成功,錯誤在位置等信息。最後經過GeneratedCode屬性,獲得生成好的C#代碼。post

    3.編譯模板

  通常Razor的C#代碼生成後,都是經過CodeDom來生成和編譯代碼的;.NET開源後,咱們又多了一個強大的選擇Roslyn , Roslyn也是支持.NET Core的,而且在整個.NET平臺中,扮演着很是重要的角色,小到這種視圖代碼編譯,大到整個項目的編譯場景都有Roslyn的身影。微軟最新開源的Visual Studio Code中C#插件,OmniSharp就是經過Roslyn來對項目和編輯器提供支持的。ui

  摘錄YOYOFx代碼以下:this

public Type Compile(string compilationContent)
{
     var assemblyName = Path.GetRandomFileName();
     var sourceText = SourceText.From(compilationContent, Encoding.UTF8);
     var syntaxTree = CSharpSyntaxTree.ParseText( sourceText, path: assemblyName, options: new CSharpParseOptions());
     var compilation = CSharpCompilation.Create(assemblyName, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
                        syntaxTrees: new[] { syntaxTree },
                        references: ApplicationReferences );
     using (var assemblyStream = new MemoryStream())
   {
var result = compilation.Emit(assemblyStream, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); this.CompileResult = new CompileResult(){ Success = result.Success, Errors = result.Diagnostics.Select(d => d.ToString()).ToList()}; if (!result.Success){ if (!compilation.References.Any() && !ApplicationReferences.Any()) throw new InvalidOperationException("project.json preserveCompilationContext"); return null; } var templateType = LoadTypeForAssemblyStream(assemblyStream, null); return templateType; } }

   在代碼中能夠經過CompileResult獲得相應的編譯錯誤信息,一樣包括錯誤的信息和具體錯誤所在的行。

  其中注意的是LoadTypeForAssemblyStream方法,由於在.NET Core中動態加載程序集的方式跟之前有所不一樣AppDomain的概念如今已經消失,因此要在.NET Core動態加載程序集要使用,命名空間System.Runtime.Loader中的AssemblyLoadContext.Default.LoadFromStream 方法,而在.NET 4.5+中要使用Assembly.Load方法。

 3、總結

   Razor 不只僅使用了動態的編譯,還有一個強大的模板解析的功能。利用自定義的模板基類還能夠在模板裏提供一些輔助方法。這樣看來 Razor 也算是 C# DSL 的一種實現了。

 

 

  GitHubhttps://github.com/maxzhang1985/YOYOFx  若是覺還能夠請Star下, 歡迎一塊兒交流。

 

  .NET Core 和 YOYOFx 的交流羣: 214741894  

 

  若是你以爲本文對你有幫助,請點擊「推薦」,謝謝.

相關文章
相關標籤/搜索