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

ASP.NET MVC脚手架生成的Controller无法创建数据库数据

ASP.NET MVC中Facility创建失败无提示问题

我在ASP.NET MVC Web应用里添加了新的脚手架项,但控制器的Create方法无法向数据库插入数据,提交后直接退回创建页面,且无任何错误提示。


控制器代码(FacilitiesController的Create方法)

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Status,Capacity,OpenedTime,ClosedTime,ServiceID")] Facility facility)
{
        if (ModelState.IsValid)
        {
            _context.Add(facility);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        ViewData["ServiceID"] = new SelectList(_context.Service, "Id", "Id", facility.ServiceID);

        return View(facility);
}

模型定义

Service与Facility为一对多关系:

public enum PricingType
{
    Item,
    Session,
    Hour,
    Day,
    Week,
    Month
}

public partial class Service
{
    public Service()
    {
        this.Facilities = new HashSet<Facility>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }

    public PricingType PricingType { get; set; }
    public ICollection<Facility> Facilities { get; set; }
}

public enum FacilityStatus
{
    Available,
    Reserved,
    Occupied,
    Cleaning,
    Maintenance,
    Unknown
} 

public class Facility
{
    public int Id { get; set; }
    public string? Name { get; set; } 
    public FacilityStatus? Status { get; set; }
    public int? Capacity { get; set; }

    [DisplayName("Opened Time")]
    [Range(0.0, 24.0)]
    public double? OpenedTime { get; set; }
    [DisplayName("Closed Time")]
    [Range(0.0, 24.0)]
    public double? ClosedTime { get; set; }
    public int ServiceID { get; set; }
    public Service Service { get; set; }
}

迁移代码(add-migration AddedFacility生成)

protected override void Up(MigrationBuilder migrationBuilder)
{
        migrationBuilder.CreateTable(
            name: "Service",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
                Price = table.Column<double>(type: "float", nullable: false),
                PricingType = table.Column<int>(type: "int", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Service", x => x.Id);
            });

        migrationBuilder.CreateTable(
            name: "Facility",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
                Status = table.Column<int>(type: "int", nullable: true),
                Capacity = table.Column<int>(type: "int", nullable: true),
                OpenedTime = table.Column<double>(type: "float", nullable: true),
                ClosedTime = table.Column<double>(type: "float", nullable: true),
                ServiceID = table.Column<int>(type: "int", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Facility", x => x.Id);
                table.ForeignKey(
                    name: "FK_Facility_Service_ServiceID",
                    column: x => x.ServiceID,
                    principalTable: "Service",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

        migrationBuilder.CreateIndex(
            name: "IX_Facility_ServiceID",
            table: "Facility",
            column: "ServiceID");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Facility");

        migrationBuilder.DropTable(
            name: "Service");
    }

控制台日志输出

Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (43ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [s].[Id], [s].[Name], [s].[Price], [s].[PricingType]
FROM [Service] AS [s]
The thread 0x1720 has exited with code 0 (0x0).
Microsoft.EntityFrameworkCore.Infrastructure: Information: Entity Framework Core 6.0.9 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.9' with options: None
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [s].[Id], [s].[Name], [s].[Price], [s].[PricingType]
FROM [Service] AS [s]
The thread 0x3680 has exited with code 0 (0x0).
The thread 0x33c has exited with code 0 (0x0).
The thread 0x4734 has exited with code 0 (0x0).
The thread 0x4b94 has exited with code 0 (0x0).
The thread 0x3220 has exited with code 0 (0x0).


视图代码(Views/Facilities/Create.cshtml)

@model HRMS.Models.Facility

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Facility</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Status" class="control-label"></label>
                <select asp-for="Status" class="form-control" asp-items="Html.GetEnumSelectList<FacilityStatus>()"></select>
                <span asp-validation-for="Status" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Capacity" class="control-label"></label>
                <input asp-for="Capacity" class="form-control" />
                <span asp-validation-for="Capacity" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="OpenedTime" class="control-label"></label>
                <input asp-for="OpenedTime" class="form-control" />
                <span asp-validation-for="OpenedTime" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ClosedTime" class="control-label"></label>
                <input asp-for="ClosedTime" class="form-control" />
                <span asp-validation-for="ClosedTime" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ServiceID" class="control-label"></label>
                <select asp-for="ServiceID" class ="form-control" asp-items="ViewBag.ServiceID"></select>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

问题分析与修复方案

核心原因

提交后退回页面说明ModelState.IsValid为false,但视图未正确显示验证错误,同时存在几个关键问题:

  1. 防伪造令牌不匹配
    控制器方法标记了[ValidateAntiForgeryToken],但视图表单未生成对应的令牌字段,导致验证失败。需在表单内添加:

    @Html.AntiForgeryToken()
    
  2. 验证摘要范围限制
    当前视图使用ModelOnly模式的验证摘要,仅显示模型级错误,改为All模式可显示所有字段级错误:

    <div asp-validation-summary="All" class="text-danger"></div>
    
  3. Service下拉框显示不合理
    控制器中ViewData["ServiceID"]的SelectList用Id作为显示文本,用户无法识别服务名称,改为Name更直观:

    ViewData["ServiceID"] = new SelectList(_context.Service, "Id", "Name", facility.ServiceID);
    
  4. 调试定位具体错误
    在控制器Create方法内添加调试代码,查看ModelState的具体错误项:

    // 在if (ModelState.IsValid)前添加
    var validationErrors = ModelState.Values.SelectMany(v => v.Errors);
    // 断点查看validationErrors的内容,定位未通过验证的字段
    

修复步骤

  1. 在Create视图的<form>标签内添加防伪造令牌
  2. 修改验证摘要为All模式
  3. 调整Service下拉框的显示文本为服务名称
  4. 通过调试查看ModelState错误,针对性修复字段验证问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 22:35:25