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