【问题标题】:Why does my razor view throw InvalidOperationException for the wrong part of the code?为什么我的 razor 视图会为代码的错误部分抛出 InvalidOperationException?
【发布时间】:2020-05-15 22:31:40
【问题描述】:

我有一个像这样循环的视图

 @foreach (var item in Model.RoutineAttachments)
 {
     <tr>
         <td>@item.Attachment.Name</td>
         <td>@item.Attachment.Weight</td>
         <td>@item.Attachment.Thickness</td>
         <td>@item.IsGeneric</td>
         <td>@Html.ActionLink("Delete", "DeleteAttachment", new { routineId = item.RoutineId, attachmentId = item.Attachment.Id }, new { @class = "btn btn-danger btn-sm" })</td>
     </tr>
 }

当我访问该页面时,我得到一个 InvalidOperationException,其中令人烦恼的部分是“序列不包含任何元素”。

完整的堆栈跟踪:

[InvalidOperationException: Sequence contains no elements]
   System.Linq.Enumerable.First(IEnumerable`1 source) +335
   ASP._Page_Views_Routine_EditAttachments_cshtml.Execute() in c:\Project\Dashboard\Views\EditAttachments.cshtml:19
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +197
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +105
   System.Web.WebPages.StartPage.RunPage() +17
   System.Web.WebPages.StartPage.ExecutePageHierarchy() +73
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +78
   System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +235
   System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +107
   System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +291
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +56
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +420
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +52
   System.Web.Mvc.Async.<>c__DisplayClass3_6.<BeginInvokeAction>b__4() +198
   System.Web.Mvc.Async.<>c__DisplayClass3_1.<BeginInvokeAction>b__1(IAsyncResult asyncResult) +100
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +48
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27
   System.Web.Mvc.<>c.<BeginExecuteCore>b__152_1(IAsyncResult asyncResult, ExecuteCoreState innerState) +11
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +48
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +45
   System.Web.Mvc.<>c.<BeginExecute>b__151_2(IAsyncResult asyncResult, Controller controller) +13
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +22
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +48
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
   System.Web.Mvc.<>c.<BeginProcessRequest>b__20_1(IAsyncResult asyncResult, ProcessRequestState innerState) +28
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +48
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.InvokeEndHandler(IAsyncResult ar) +161
   System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +128

我在从控制器返回和进入循环时检查了集合不为空,以确保这不是问题。但是一旦它试图获取循环中的第一项,我就会得到异常。这不应该发生,因为它是一个 foreach 循环。

但是,在视图的更下方,我有这部分:

<input type="hidden" value=@Model.RoutineAttachments.First().RoutineId />

改成:

<input type="hidden" value=@Model.RoutineAttachments.FirstOrDefault()?.RoutineId />

解决了根本问题。

然而,我的问题是,为什么在访问 foreach 循环时抛出异常,然后显示不正确的堆栈跟踪,而不是在访问不存在的项目时抛出?

编辑: 由于人们回答了错误的问题,因此添加了更多证据。请尝试回答实际问题。

编辑 2:

我创建了一个最小的可复制版本

控制器:

public class TestController : Controller
{
    // GET: Test
    public ActionResult Index()
    {
        TestModel t = new TestModel();
        t.TestList = new List<TestItem>();
        return View(t);
    }
}

public class TestModel
{
    public List<TestItem> TestList { get; set; }
}

public class TestItem
{
    public string S { get; set; }
}

查看:

@model Dashboard.Controllers.TestModel



@foreach (var item in Model.TestList)
{
    <h5>@item.S</h5>
}


<input type="hidden" value="@Model.TestList.First().S" />

似乎由于某种原因,它在 foreach 循环之前用 First() 解析了该行,但在访问 foreach 循环之前不会抛出异常?

【问题讨论】:

  • 向我们展示您的控制器的操作。这样就更容易确定实际问题是什么。
  • 在Model构造函数中初始化RoutineAttachments是否有效?类似RoutineAttachments = new List&lt;Attachment&gt;()。视图顶部是否正确定义了模型类型?
  • 另外请展示您的模型和整个视图
  • 是的,模型在视图顶部正确定义。并且该列表已初始化,因为它不为空且仅为空。
  • 视图也几乎只是我已经展示的内容。还有一些 div 等等。

标签: c# asp.net linq asp.net-mvc-5


【解决方案1】:

我能够使用提供的最少示例代码重现错误,但只能通过运行“发布”构建配置。运行“调试”配置时,显示 ASP.Net 错误页面错误源自预期的行(包含对First() 的调用的行)。

看起来混乱是由于编译器优化造成的。默认情况下,为“发布”构建启用优化,而不是为“调试”构建启用优化。启用优化后,编译器本质上可能是rewrite code to make it more efficient。然后,代码可以在程序执行时由即时(“JIT”)编译器再次重写。启用优化后,执行的代码可能看起来与原始源代码有很大不同——差异很大,以至于无法提供带有映射回这些源代码的行号的堆栈跟踪。

就普通开发人员而言,编译器优化是巫术。几乎不可能猜测给定的代码将如何优化。如果你有足够的动力,你可以检查优化后的代码,看看有什么变化——thisthis 的答案(都是同一个问题的答案)给出一些建议。

对于它的价值,我快速浏览了反编译的视图,并没有发现任何明显奇怪的东西。这表明 JIT 编译器是罪魁祸首。 (不过,我不是编译器优化方面的专家,所以我可能错了)

无论如何...确保您使用的是“调试”配置(或禁用编译器优化的其他配置),我认为问题会消失。

【讨论】:

    【解决方案2】:

    似乎混乱是 Razor 页面的呈现顺序。在页面上呈现所有控件之前,不会呈现和评估 foreach 循环。
    简而言之,在渲染 Razor 代码块之前渲染 Html。 有关该过程的简要说明,请参见此问题: What is the execution order of an MVC Razor view/layout 如上所述,异常显然是对 First 的 linq 调用。我想,由于在渲染过程中出现异常,页面是不完整的,它可能只是这种状态的一个条件,它显示当前执行是在 foreach 循环而不是控件上。

    【讨论】:

      【解决方案3】:

      似乎由于某种原因,它在 foreach 循环之前用 First() 解析了该行,但在访问 foreach 循环之前不会抛出异常?

      不是这样的,foreach循环一执行(本例不往里走),下一条语句

      <input type="hidden" value="@Model.TestList.First().S" />
      

      被执行(由 Razor 引擎渲染)。

      您可以通过以下代码确认。

       public class TestController : Controller
          {
              // GET: Test
              public ActionResult Index()
              {
                  TestModel t = new TestModel();
                  t.TestList = new List<TestItem>();
                  return View(t);
              }
          }
      
          public class TestModel
          {
              public List<TestItem> TestList { get; set; }
      
              public string AnyMethod()
              {
                  return "";
              }
          }
      
          public class TestItem
          {
              public string S { get; set; }
          }
      

      在Index.cshtml中

        @model Dashboard.Controllers.TestModel
      
      @{
      
          var testObject = new Dashboard.Controllers.TestModel();
      }
      
      
      @foreach (var item in Model.TestList)
      {
          <h5>@item.S</h5>
      }
      
      
      <input type="hidden" value="@testObject.AnyMethod()" />
      

      现在在

      上放一个断点
      public string AnyMethod()
       {
         return "";
         // You may throw InvalidOperationException to see the stack trace
       }
      

      您会注意到,一旦执行了 foreach,就会执行下一个方法 AnyMethod 或在您的情况下是 TestList.First() 引发异常。为什么 TestList.First() 抛出异常可以在 andrés matínez 的回答中阅读

      【讨论】:

        【解决方案4】:

        这就是“First()”的工作原理:https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.first?view=netframework-4.8

        当你使用“First”时,这将返回列表的第一个元素,如果没有元素,这将给你一个 InvalidOperationException”,因为你做了一个无效的操作。“First()”不检查是否在返回您的值之前元素是否存在。

        【讨论】:

        • 当然。那不是我的问题。我的问题是为什么我在访问 foreach 循环时遇到异常
        • @John 提供的答案是正确的。您的例外是 System.Linq.Enumerable.First(IEnumerable`1 source) +335 第二行。它不是由 foreach 循环触发的。
        • 堆栈跟踪指向foreach循环所在的行,调试razor视图在步入循环时弹出异常。
        • @AlexLeo 我已经更新了一张图片,向您展示确实是触发它的 foreach 循环。
        • 没有。如果集合为空,那将抛出 NullReferenceException。该集合为空且不为空,因此它应该继续通过 foreach 循环,然后在访问 First() 时抛出 InvalidOperationException 进一步向下。现在它在迭代循环时抛出 InvalidOperationException 而不是 NullReferenceException,正如您在 gif 中看到的那样
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-07-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多