ASP.NET MVC使用動態類型Model


解決的是在ASP.NET MVC使用dynamic類型Model時遇到的一個真實問題。C# 4編譯器支持dynamic類型,因此在編寫頁面模板的時候自然就可以把它作為視圖的Model類型。表現層的需求很容易改變,因此dynamic類型的Model可以減少我們反復修改強類型Model的麻煩,再配合匿名類型的使用,可謂是動靜相宜,如魚得水。不過,如果把一個匿名類型直接作為Model交給視圖去使用,在默認情況下會拋出異常。

模擬問題:

建立Controller:

    public class HomeController : Controller
    {
        internal string Content { get; set; }
        public ActionResult Index()
        {
            //動態實體模型,交給View顯示
            return View(new {Title="你好"});
        }

        public ActionResult About()
        {
            return View();
        }
    }

添加View:

@{
    ViewBag.Title = "Home Page";
}
<h2>標題為:@Model.Title</h2>

按理來說,這么做應該一切正常,但是運行之后便會提示說Model上找不到Title成員,如圖:

問題分析:

  動態類型與靜態類型有可能在生成dll過程中有細微的區別,因為通常我們用的都是靜態對象,在MVC中也多數用強類型做為視圖模型,一直過的都很太平,所以這種問題很少遇到,今天我們研究一下,自娛自樂嘛!

  既然懷疑動態類型在生成的過程中有什么不為人知的東西,哪我們就得想辦法驗證一下,以證清白。動態類型編譯過程咱們是不可能看到的,也不可能跟蹤的,但我們可以從結果入手,反編譯回去,看一看它的廬山真面目。

  反編譯利器我選擇ILSpy,主要我就會用這個利器,呵呵,用ILSpy查看MVC生成程序集,我們看到了驚人的一幕。

  只到現在我們才豁然開朗,由於是“匿名類型”,顯然它的訪問級別應該是internal的,這樣它就能對外“隱藏”起來了。但是這就給ASP.NET MVC的視圖帶來了麻煩。因為ASP.NET MVC的視圖會在運行時動態地編譯cshtml為額外的dll,因此它是無法訪問到Controller所在程序集的internal成員的。

對症下葯

  Mono.Cecil是Mono的組件之一,用來編輯.NET程序集文件。我們可以用它來打探一個.NET程序集內部的結構,就像反射那樣,只不過並不需要將程序集加載進來,Mono.Cecil只是讀取文件物理內容而已。例如,上圖所用的ILSpy便用到了Mono.Cecil。更重要的是,Mono.Cecil可以修改並保存程序集,這便可以讓我們實現各種奇形怪狀的要求。

創建一個名為DynamicBuilderApplication的控制台項目,並選擇Reference - Manage NuGet Packages

 

有了Mono.Cecil我們便可以修改程序集了,只需數行代碼:

View Code
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var asmFile = args[0];
 6             Console.WriteLine("Making anonymous types public for '{0}'.", asmFile);
 7             var asmDef = AssemblyDefinition.ReadAssembly(asmFile, new ReaderParameters { ReadSymbols = true });
 8             var anonymousTypes = asmDef.Modules.SelectMany(m => m.Types).Where(t => t.Name.Contains("<>f__AnonymousType"));
 9             foreach (var type in anonymousTypes)
10             {
11                 type.IsPublic = true;
12             }
13             asmDef.Write(asmFile, new WriterParameters { WriteSymbols = true });
14         }
15     }

首先,從參數中獲取需要修改的程序集名稱,找到所有的匿名類型,並將其訪問級別設為Public后保存。保存的時候將WriteSymbols參數設為true,這樣它也會同時修改pdb文件——這很重要,否則修改后的程序集無法和pdb文件內容相對應,便無法調試了。換句話說,Mono.Cecil也能正確處理pdb文件。

  最后,只要在ASP.NET MVC網站編譯時使用這個項目即可,只需配置一下它的Post Build事件:

再次編譯並運行程序:

再拿ILSpy來檢查一番:

到此看來這個問題被我們解決了。


免責聲明!

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



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