【问题标题】:Weird razor behavior with List<dynamic>List<dynamic> 的奇怪剃刀行为
【发布时间】:2015-02-24 21:23:46
【问题描述】:

这是我的控制器的代码:

 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' - 但这些答案都没有帮助我。

接受的答案是:“现在 MVC 3 直接支持动态,不再需要下面的技术”,所以我也尝试将投影的 List&lt;dynamic&gt; 返回到视图( “准备使用,无需投影”)并且它也不起作用(得到相同的错误)。这是该尝试的代码:

控制器代码:

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

 return View(foos);

查看代码:

 @model dynamic

 etc.




编辑:使用调试器,我可以检查(在抛出异常之后)该项目确实有"a definition for..."(在示例代码中,项目是x,但这里是lot

【问题讨论】:

  • 你在调试器中单步执行了吗?我很好奇 projectedx 的类型是什么。
  • 在调试器中单步执行。你能保证延迟执行不会杀死你吗? (即使您似乎在第二次尝试中实现了该列表..)
  • 在调试模式下跳转错误时,我完全可以在foreach中查看所有“x”的内容。我可以验证 fooId 是否存在(调试器能够读取它,即使错误告诉我该对象不包含它的定义......)
  • 我添加了调试器截图

标签: asp.net-mvc razor


【解决方案1】:

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

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

这也解释了为什么当您在 Razor 视图中创建匿名类型时您的代码可以正常工作。然后,您使用 dynamic 生成的代码能够反映匿名类型,因为它是在同一个程序集中声明的。

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

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

【讨论】:

  • 但是为什么说“从mvc开始支持3个动态模型”呢? (这在我在我的问题中链接的帖子中接受的答案中说)
  • @sports:因为从 MVC 3 开始,您可以在视图中指定 @model dynamic。但问题的根本原因不是dynamic 的使用,而是视图中不可用的匿名类型。尝试使用 dynamic 访问匿名类型不允许您解决匿名类型为 internal 的问题。
  • 当您说“在 MVC Razor 中,当在控制器中声明匿名类型时,您不能在视图中使用它们”:好吧,在我的示例中,控制器中没有创建匿名类型。 IQueryable&lt;Foo&gt; 正确传递给视图,Foo.Projection()Foo 内部的静态方法。 Foo 不是控制器。所以......最后:我不能重用那个投影?
  • @sports:我假设Foo 类是在与控制器相同的程序集中声明的,我只是想让我的解释保持简单。问题仍然相同:Foo.Projection 中创建的匿名类型与Foo 类在同一个程序集中,并且它是内部的,因此无法从视图创建的程序集中访问它。匿名类型不能在已声明它们的程序集之外使用,并且在 MVC Razor 中,视图位于单独的程序集中。我建议了两种方法来解决这个问题,最简单的方法是切换到非匿名类型。
【解决方案2】:

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

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

但是,如果你不使用强类型ViewModel 的任何优点,比如类型检查和@987654326,为什么还要使用强类型View @?

如果你真的想将动态类型传递给你的View,因为MVC 3你可以使用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 />
}

【讨论】:

  • 我认为它不知道使用View() 的哪个构造函数,因为调试器能够正确识别模型(已经站在视图中)。如果有问题,我可以使用View(model: ...)。关于建议的解决方案,我尝试了 ViewBag 但也没有工作:-(,它再次通过调试器识别,但抛出的异常是相同的:对象不包含....
【解决方案3】:

在控制器内部 List foos = dbContext.Foos.Select(Foo.Projection()).ToList();

 return View(foos);

在剃刀侧视图中

@model List<dynamic>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多