Почему мое представление бритвы вызывает InvalidOperationException для неправильной части кода? - PullRequest
0 голосов
/ 30 января 2020

У меня есть View с al oop, подобным этому

 @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 с неприятной частью «Последовательность не содержит элементов».

Full трассировка стека:

[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

Я проверил, что коллекция не равна нулю при возврате из контроллера и при входе в l oop, чтобы убедиться, что это не проблема. Но как только он пытается получить первый элемент в l oop, я получаю исключение. Чего не должно случиться, потому что это foreach l oop.

Тем не менее, ниже в представлении у меня есть эта часть:

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

Изменение на:

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

Исправляет основную проблему.

Мой вопрос, однако, почему исключение генерируется при доступе к foreach l oop, а затем отображает неверную трассировку стека вместо броска при доступе к несуществующему элементу?

РЕДАКТИРОВАТЬ: Добавление дополнительных доказательств, так как люди отвечают на неправильный вопрос. Пожалуйста, попробуйте ответить на актуальный вопрос.

Stacktrace showing foreach loop

Exception when stepping into foreach loop

РЕДАКТИРОВАТЬ 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" />

Кажется, что по какой-то причине он анализирует строку с First () перед foreach l oop, но не выдает исключение, пока не получит доступ к foreach l oop?

Ответы [ 4 ]

2 голосов
/ 10 февраля 2020

Кажется, путаница - это порядок рендеринга страницы Razor. Foreach l oop не будет отображаться и оцениваться до тех пор, пока все элементы управления не будут отображены на странице.
Короче говоря, Html отображается до того, как будут отображены блоки кода Razor. Посмотрите этот вопрос для краткого, хорошего объяснения процесса: Каков порядок выполнения MVC Razor view / layout Исключение явно при вызове linq для First, как объяснено выше. Я бы предположил, что, так как исключение попадает во время рендеринга, страница является неполной, и это может просто быть условием этого состояния, которое заставляет казаться , что выполнение в настоящее время находится на foreach l oop вместо контроль.

2 голосов
/ 09 февраля 2020

Мне удалось воспроизвести ошибку, используя минимальный предоставленный пример кода, но только при запуске конфигурации сборки «Release». При запуске конфигурации «Debug», ASP. Net Страница ошибок показала, что ошибка возникла в ожидаемой строке (той, которая содержит вызов First()).

Похоже, что путаница связана с оптимизацией компилятора. По умолчанию оптимизация включена для сборок "Release", а не для сборок "Debug". Когда оптимизации включены, компилятор может, по существу, переписать код, чтобы сделать его более эффективным . Затем код может быть переписан заново компилятором Just-in-Time ("JIT") по мере выполнения программы. Когда оптимизация включена, код, который выполняется, может значительно отличаться от исходных источников - настолько, что невозможно дать трассировку стека с номерами строк, которые сопоставляются с этими источниками.

Оптимизация компилятора, как Что касается среднего разработчика, вуду. Почти невозможно догадаться, как данный фрагмент кода будет оптимизирован. Если вы достаточно мотивированы, вы можете проверить оптимизированный код, чтобы увидеть, что изменилось - этот и этот ответ (оба являются ответами на один и тот же вопрос) дают несколько советов о том, как это сделать.

Что бы это ни стоило, я быстро взглянул на декомпилированный вид и не заметил ничего странного. Это говорит о том, что виновником является JIT-компилятор. (Я не эксперт по оптимизации компилятора, поэтому я могу ошибаться)

В любом случае ... убедитесь, что вы используете конфигурацию "Debug" (или другую конфигурацию с отключенной оптимизацией компилятора) и я думаю, что проблема исчезнет go.

0 голосов
/ 07 февраля 2020

Кажется, что по какой-то причине он анализирует строку с First () перед foreach l oop, но не выдает исключение, пока не получит доступ к foreach l oop?

Это не так, как только выполняется foreach l oop (в данном случае не внутри), выполняется следующая инструкция

<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.cs html

  @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

0 голосов
/ 30 января 2020

Вот как работает «First ()»: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.first?view=netframework-4.8

Когда вы используете «First», это возвращает первый элемент списка, и если нет элемента , это даст вам InvalidOperationException "потому что вы сделали недопустимую операцию." First () "не проверяет, существует ли элемент или нет, прежде чем возвращать ваше значение.

...