List <dynamic> </dynamic>的奇怪剃刀行为

时间:2015-02-24 21:23:46

标签: asp.net-mvc razor

这是我的控制器代码:

 IQueryable<Foo> foos = dbContext.Foos.Where(...);

 return View(foos);

这个剃刀代码(cshtml)可以很好

@model IQueryable<Foo>

@{
     IQueryable<Foo> foos = Model;

     var projected = foos.Select(e => new 
     {
          fooId = e.FooId,
          bar = new 
          {
              barId = e.Foo.BarId
          }
     }).ToList();
}

@foreach (var x in projected)
{
     <span>@x.fooId</span><br />
}

但是这个剃刀代码(cshtml)不起作用,几乎是一样的东西!:

@model IQueryable<Foo>

@{
     IQueryable<Foo> foos = Model;

     var projected = foos.Selected(Foo.Projection()).ToList()
}

@foreach (var x in projected)
{
     <span>@x.fooId</span><br />
}

Foo.Projection()是一种静态方法,我重复使用了很多:

    public static Expression<Func<Foo, dynamic>> Projection()
    {
        return e => new
        {
            fooId = e.FooId,
            bar = new 
            {
                barId = e.Foo.BarId
            }
        }
    }

我得到了那个着名的错误:'object' does not contain definition for 'fooId',这里讨论的是MVC Razor dynamic model, 'object' does not contain definition for 'PropertyName' - 但这些答案都没有帮助我。

接受的答案说:&#34; 现在MVC 3直接支持动态,下面的技术已不再需要&#34;,所以我也尝试返回预计的{ {1}}到视图(&#34;准备使用,无需投影&#34;)并且它也没有工作(获得相同的错误)。这是该尝试的代码:

控制器代码:

List<dynamic>

查看代码:

 List<dynamic> foos = dbContext.Foos.Select(Foo.Projection()).ToList();

 return View(foos);




编辑:使用调试器,我能够检查(在抛出异常之后)项目确实具有 @model dynamic etc. (在示例代码中项目为"a definition for...",但这里是{{ 1}})

Debugger proof

3 个答案:

答案 0 :(得分:6)

当您使用dynamic时,您指示编译器使用反射来调用方法和访问属性。在您的情况下,您以这种方式访问​​的对象是匿名类型,匿名类型是internal到它们创建的程序集。

为Razor视图生成的代码位于单独的程序集中,并且尝试反映在控制器中创建的匿名类型将失败。调试器不受此限制的影响,因此当反射失败并抛出异常时,您仍然可以检查调试器中匿名类型的属性。

这也解释了为什么在Razor视图中创建匿名类型时代码有效。然后,使用dynamic生成的代码能够反映出不同类型,因为它是在同一个程序集中声明的。

基本上,在MVC Razor中,当它们在控制器中声明时,您无法在视图中使用匿名类型。您对dynamic的使用通过生成难以理解的运行时错误来隐藏此基本问题。

要解决您的问题,您可以创建特定的公共类型,而不是使用内部匿名类型,或者您可以在控制器中convert the anonymous type to an ExpandoObject

答案 1 :(得分:1)

我认为View()构造函数只是不知道要使用什么重载,因为你有dynamic类型。您可以尝试这样做:

List<dynamic> foos = dbContext.Foos.Select(Foo.Projection()).ToList();
ViewData.Model = foos; 
return View();

但是,如果您不使用任何强类型View优势 ,为什么要使用强类型ViewModel,比如类型检查和IntelliSense

如果您确实要将动态类型传递到{strong> MVC 3 之后的View,则可以使用ViewBag dynamic

Controller

List<dynamic> foos = dbContext.Foos.Select(Foo.Projection()).ToList();
ViewBag = foos; 
return View();

View

@foreach (var x in ViewBag)
{
     <span>@x.fooId</span><br />
}

答案 2 :(得分:0)

控制器内部     列表foos = dbContext.Foos.Select(Foo.Projection())。ToList();

 return View(foos);

在侧剃刀视图中

@model List<dynamic>