问题起因

在 Controller 中定义动态类型的对象传递给视图,报错无法找到成员。

Controller:

1
2
3
4
5
public ActionResult Index()
{
    ViewBag.User = new { name = "何湘辉", age = 20 };
    return View();
}

视图:

1
2
姓名:@ViewBag.User.name
年龄:@ViewBag.User.age

按道理来说,这样写是没问题的,但是运行后却说Model中不存在‘name’。

然后经过一番百度才找到了答案,因为 dynamic 是“匿名类型”,它的访问级是 internal,所以只有在同一程序集的文件中才是可以访问的。ASP.NET MVC 在编译的时候,会将 cshtml 视图和 Controller 分别编译成两个 DLL 文件,因此视图是无法访问到 Controller 所在程序集的 internal 成员的。

解决办法

第一种解决办法是使用 Moon.Cecil 修改程序集,将所有匿名类型的访问级别修改成 public。可以说是从问题根源解决了问题,但是这个方法我并没有成功,我也不知道哪里出错了,所以就不讲详细过程了。想了解的话可以看看使用Mono.Cecil辅助ASP.NET MVC使用dynamic类型Model

第二种解决办法是使用ExpandoObject类型。ExpandoObject 是一种可以在运行时动态添加、删除成员的类型,并且可以跨程序集访问。

使用方法:

1
2
3
4
5
6
7
8
public ActionResult Index(

    dynamic expandoObj = new ExpandoObject(); 
    expandoObj.name= "何湘辉"
     expandoObj.age = 20;
     ViewBag.User = expandoObj;
    return View(); 
}

这种方法虽然解决了跨程序集访问的问题,但是每次都要 new 一个 ExpandoObject 对象,然后再为它手动添加成员,就显得有点麻烦了,所以就有了第三种方法。

第三种方法是先将匿名对象转换成 JSON 格式的字符串,然后再将 JSON 字符串转换成 JSON 对象。

这里我用的是 Newtonsoft.Json 来对 JSON 进行操作。

1
2
3
4
5
6
7
8
9
10
public ActionResult Index(

    var varObj = new { name = "何湘辉", age = 20};
    // 将匿名对象转换成JSON字符串 {"name": "何湘辉", "age": 20}
    string jsonStr = JsonConvert.SerializeObject(varObj);
    // 再将JSON字符串转换成object
    object obj = JsonConvert.DeserializeObject(jsonStr);
    ViewBag.User = obj;
    return View(); 
}

这是一篇过去很久的文章,其中的信息可能已经有所发展或是发生改变。