I have a View with a loop like this
@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>
}
When I access the page I get an InvalidOperationException with the vexing part "Sequence contains no elements".
Full stack trace:
[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
I checked that the collection isn't null when returning from the controller and when stepping into the loop to be sure that wasn't the issue. But as soon as it tries to get the first item in the loop, I get the exception. Which shouldn't happen since it's a foreach loop.
However, further down in the view I have this part:
<input type="hidden" value=@Model.RoutineAttachments.First().RoutineId />
Changing it to:
<input type="hidden" value=@Model.RoutineAttachments.FirstOrDefault()?.RoutineId />
Fixes the underlying issue.
My question however is, why the exception gets thrown when accessing the foreach loop and then displays an incorrect stacktrace, instead of throwing when accessing the non existing item?
EDIT: Adding more proof since people are answering the wrong question. Please try to answer the actual question.
EDIT 2:
I have created a minimal reproducible version
Controller:
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; }
}
View:
@model Dashboard.Controllers.TestModel
@foreach (var item in Model.TestList)
{
<h5>@item.S</h5>
}
<input type="hidden" value="@Model.TestList.First().S" />
It's seems that for some reason it parses the line with First() before the foreach loop, but doesn't throw the exception until accessing the foreach loop?