You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core Razor中asp-for绑定集合元素引发索引越界异常及验证问题

Asp.Net Core Razor页面集合字段索引越界与验证失效问题

模型类代码

public class EmployeeInfo
{
    public int EmployeeInfoId { get; set; }
    public string LastName { get; set; } = null!;
    public string EmailAddress { get; set; } = null!;
    public virtual List<EmergencyInfo> EmergencyInfos { get; set; } = new List<EmergencyInfo>();
}

public partial class EmergencyInfo
{
    public int EmergencyInfoId { get; set; }

    public int EmployeeInfoId { get; set; }
    [DisplayName("Last Name")]
    [Required]
    public string? LastName { get; set; } = null!;
}

Razor页面代码

<div class="form-group">
    @if (Model != null && Model.EmergencyInfos != null && Model.EmergencyInfos.ToList().Count > 0)
    {
        var count = Model.EmergencyInfos.Count;
        @for (var i = 0; i < count; i++)
        {
            <div class="form-group row">
                <div class="col">
                    <label class="control-label">LastName</label>
                    <input asp-for="EmergencyInfos[i].LastName" value="@(string.IsNullOrEmpty(Model.EmergencyInfos.ToList()[i].LastName)? "": @Model.EmergencyInfos.ToList()[i].LastName)" class="form-control" />/>
                    <span asp-validation-for="EmergencyInfos[i].LastName" class="text-danger"></span>
                </div>
           </div>  
        }
    }
    else
    {
       
        <div class="form-group row">
            <div class="col">
                <label asp-for="EmergencyInfos[0].LastName" class="control-label">LastName</label>
                 <input asp-for="EmergencyInfos[0].LastName" class="form-control" />
                <span asp-validation-for="EmergencyInfos[0].LastName" class="text-danger"></span>
            </div>
         </div>
       
    }
</div>

问题现象

使用上述Razor视图时,触发索引越界异常,错误截图如下:
错误截图

异常信息:

An unhandled exception occurred while processing the request.
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
lambda_method172(Closure , EmployeeInfo )

Stack Query Cookies Headers Routing
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
lambda_method172(Closure , EmployeeInfo )
Microsoft.AspNetCore.Mvc.ViewFeatures.ExpressionMetadataProvider+<>c__DisplayClass0_0<TModel, TResult>.<FromLambdaExpression>g__modelAccessor|0(object container)
Microsoft.AspNetCore.Mvc.ViewFeatures.ModelExplorer.get_Model()
Microsoft.AspNetCore.Mvc.ViewFeatures.ModelExplorer.get_ModelType()
Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper.GetInputTypeHints(ModelExplorer modelExplorer)+MoveNext()
Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper.GetInputType(ModelExplorer modelExplorer, out string inputTypeHint)
Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper.Process(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.TagHelpers.TagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.RunAsync(TagHelperExecutionContext executionContext)
AspNetCoreGeneratedDocument.Views_Employee_Create.<ExecuteAsync>b__21_0() in Create.cshtml
+
                                <input asp-for="EmergencyInfos[0].LastName" class="form-control" />
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder)
Microsoft.AspNetCore.Mvc.TagHelpers.RenderAtEndOfFormTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count)
AspNetCoreGeneratedDocument.Views_Employee_Create.ExecuteAsync() in Create.cshtml
+
    ViewData["Title"] = "Create";
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|22_0(ResourceInvoker invoker, IActionResult result)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

若将asp-for="EmergencyInfos[0].LastName"修改为name="EmergencyInfos[0].LastName",索引越界问题消失,但EmergencyInfo.LastName上标记的[Required]验证规则失效,无法触发“Last Name field is required”的提示。


解决方案

问题根源

当EmergencyInfos集合为空时,直接使用EmergencyInfos[0].LastName会触发索引越界——因为集合中没有对应索引的元素。而asp-for标签助手需要访问模型实例来获取元数据(如验证规则),因此必须保证集合中有对应索引的元素存在。

方案1:后端初始化模型(推荐)

在页面的Get请求处理方法中,给EmployeeInfo.EmergencyInfos添加一个空的EmergencyInfo实例,确保集合至少有一个元素:

public IActionResult OnGet()
{
    var model = new EmployeeInfo();
    // 添加空实例,避免视图中索引越界
    model.EmergencyInfos.Add(new EmergencyInfo());
    return Page(model);
}

此时可以简化Razor视图,不需要else分支,直接遍历集合即可:

<div class="form-group">
    @if (Model != null && Model.EmergencyInfos != null)
    {
        for (var i = 0; i < Model.EmergencyInfos.Count; i++)
        {
            <div class="form-group row">
                <div class="col">
                    <label asp-for="EmergencyInfos[i].LastName" class="control-label"></label>
                    <input asp-for="EmergencyInfos[i].LastName" class="form-control" />
                    <span asp-validation-for="EmergencyInfos[i].LastName" class="text-danger"></span>
                </div>
            </div>  
        }
    }
</div>

方案2:视图内动态添加空元素

若不想在后端修改,也可以在视图中先检查集合状态,为空则添加空实例:

@{
    if (Model != null && Model.EmergencyInfos != null && Model.EmergencyInfos.Count == 0)
    {
        Model.EmergencyInfos.Add(new EmergencyInfo());
    }
}
<div class="form-group">
    @if (Model != null && Model.EmergencyInfos != null)
    {
        for (var i = 0; i < Model.EmergencyInfos.Count; i++)
        {
            <div class="form-group row">
                <div class="col">
                    <label asp-for="EmergencyInfos[i].LastName" class="control-label"></label>
                    <input asp-for="EmergencyInfos[i].LastName" class="form-control" />
                    <span asp-validation-for="EmergencyInfos[i].LastName" class="text-danger"></span>
                </div>
            </div>  
        }
    }
</div>

额外优化点

原视图中多次调用Model.EmergencyInfos.ToList()是多余的,因为EmergencyInfos本身就是List<EmergencyInfo>类型,直接使用Model.EmergencyInfos[i]即可,无需重复转换。


内容的提问来源于stack exchange,提问作者Anjali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 19:29:44