利用Razor在ASP.NET MVC中的實現,自訂視圖引擎架構(2)

來源:互聯網
上載者:User

ASP.NET MVC3開始使用Razor作為其視圖引擎,取代了原來ASP.NET Web Form引擎。筆者最近研究了一下MVC3對Razor的實現,從中找到一個切入點,能夠讓我們自訂基於Razor文法的視圖解析引擎。在項目裡面可以用於諸如郵件模板定製等方面。目前,只是一個demo版本,還在進一步完善中。CodePlex : http://codeof.codeplex.com/SourceControl/list/changesets 其中的RazorEx

目前支援的功能:

1.支援Razor文法(基本的@文法)的模板檔案解析
2.支援Layout / Renderbody文法
3.支援類似asp.net 動態編譯機制,在程式運行期間,如果模板檔案變了,無需重新編譯
4.支援名字空間引用配置
5.支援複雜的程式集參考關聯性

在利用Razor在ASP.NET MVC中的實現,自訂視圖引擎架構(1)中,介紹了如何利用微軟實現的System.Web.Razor來解析基於Razor文法的模板,最後得到一個編譯單元或者源碼。本文介紹如何在代碼中對編譯單元或者源碼進行動態編譯,並執行。

應該沒有比動態編譯更靈活的了,它允許我們動態建立程式碼並編譯執行。儘管它靈活,但是實現複雜,並且效率不高,不到萬不得已不要考慮。而在這個case中卻不得不用這種方式,因為模板是使用者建立的,我們永遠不可能預知:

在.net中,System.CodeDom.Compiler.CodeDomProvider提供了將一個或多個來源程式或編譯單元編譯成程式集的方法。在.net4.0中Microsoft.CSharp.CSharpCodeProvider繼承了上面這個類,並給予了實現。有了CSharpCodeProvider,編譯一個動態程式集十分容易:

CSharpCodeProvider provider = new CSharpCodeProvider();            CompilerParameters c_options = new CompilerParameters();            c_options.IncludeDebugInformation = false;            c_options.GenerateExecutable = false;            c_options.GenerateInMemory = true;            c_options.ReferencedAssemblies.Add( "System.dll" );            CompilerResults results = provider.CompileAssemblyFromSource(c_options, code);

上面的程式碼片段,執行個體化了一個CSharpCodeProvider以及一個CompilerParameters。前者用於編譯,後者用於指定編譯時間的一些選項,如上面的代碼的設定。最後調用CompileAssemblyFromSource,傳入編譯選項和原始碼文本即可。另外CSharpCodeProviderCompileAssemblyFromDom重載,可以接受編譯選項對象和

CodeCompileUnit對象作為參數。在上一篇中,我們知道RazorTemplateEngine.GenerateCode方法返回的剛好是包含了CodeCompileUnit的對象,所以我們將使用CompileAssemblyFromDom方法

在傳回值CompilerResults.CompiledAssembly中,我們可以訪問到編譯結果的Assembly對象,再結合反射即可執行編譯代碼。另外,在編譯過程中,如果編譯失敗將拋出異常。

在引擎的開發過程中,除了上一篇和上述需要知道的基本內容外,分別有以下問題需要解決:

1、動態編譯時間需要知道引用哪些dll,否則將無法編譯成功。比如在模板裡面我引用了一個複雜物件,這個對象定義顯然不在引擎的程式集中,可能是使用者自己的程式集,或是使用者程式集引用的程式集。這就帶來了一個問題,在編譯時間我們如何知道要引用哪些程式集?我用了一個比較笨的方案:從GetCallingAssembly開始,把相互依賴的程式集遍曆一遍,並且全部在編譯時間引用。代碼中的AssemblyReferenceResolver就實現了這個功能;

2、如上面的問題,編譯的時候,源碼需要有正確的命名空間的引用,否則即使引用的程式集,還是不能編譯成功。為此,模仿mvc的實現,設計了一個configuration,添加下面這樣的設定檔即可:

<configuration>  <configSections>    <section name="RazorTemplateEngineImportNamespace"              type="RazorTemplateEngine.ImportNamespaceResolver,RazorTemplateEngine"/>  </configSections>  <RazorTemplateEngineImportNamespace>    <add namespace="ModelTest"/>    <add namespace="System.Collections.Generic"/>  </RazorTemplateEngineImportNamespace></configuration>

3、一個模板對應一個類,也就對應一個程式集,如果反覆解析模板會反覆編譯,這樣會很大程度上影響效率。解決方案是使用緩衝:將編譯過的dll和模板檔案存成字典,如果已經編譯過了並且模板檔案的最後更新時間不晚於dll的建立時間,則直接返回之前編譯的程式集;否則就進行編譯。代碼中的DynamicAssemblyCache類就實現這個功能;

4、如何?模板嵌套。在基類__TemplatePage中加入下面屬性和方法:

        private string _layout;        public virtual string Layout {            get { return _layout; }            set {                _layout = value;            }        }        public string ChildBody { get; set; }        public virtual string RenderBody()        {            if(ChildBody != null)                return ChildBody;            return string.Empty;        }

這樣類似Layout=””  @RenderBody的文法就可以通過編譯。配合遞迴的Execute即可實現。

 

 

項目現已實現基本的功能,我打算過一段試用期過後Release一個版本。源碼在上面的CodePlex上,代碼不多,還有待重構,有興趣的同仁可以和我討論,希望能實現一個健壯的引擎。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.