更新含外键的SpeakerForm模型时出现临时值错误求助
问题描述
尝试从数据库获取SpeakerForm模型,修改后通过表单提交更新。此前使用_context.Attach(Model).State = EntityState.Modified;方法能正常工作,但在新页面复用该方法时,出现如下错误:
错误信息
InvalidOperationException: The property 'SpeakerForm.SpeakerFormId' has a temporary value while attempting to change the entity's state to 'Modified'. Either set a permanent value explicitly, or ensure that the database is configured to generate values for this property. Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges, bool modifyProperties) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, bool acceptChanges, bool modifyProperties, Nullable<EntityState> forceStateWhenUnknownKey, Nullable<EntityState> fallbackState) Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry.set_State(EntityState value) LosBarriosEvents.Pages.SpeakerFormsEditModel.OnPostAsync() in Edit.cshtml.cs + _context.SpeakerForms.Attach(Form).State = EntityState.Modified;
模型定义
SpeakerForm主模型
using System.ComponentModel.DataAnnotations; namespace LosBarriosEvents.Models; public class SpeakerForm { [Key] public int SpeakerFormId { get; set; } public string Title { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string Duration { get; set; } = string.Empty; //Navigation Properties public string SpeakerId { get; set; } public Speaker? Speaker { get; set; } }
关联的Speaker外键模型
using System.ComponentModel.DataAnnotations; namespace LosBarriosEvents.Models { public class Speaker { [Key] public string SpeakerId { get; set; } = default!;//PK public string firstName { get; set; } = string.Empty; public string lastName { get; set; } = string.Empty; public string email { get; set; } = string.Empty; public string phoneNumber { get; set; } = string.Empty; //Navigation Properties public List<SpeakerForm>? SpeakerForms { get; set; } } }
后端代码(Edit.cshtml.cs)
using System.Security.Claims; using LosBarriosEvents.Areas.Identity.Data; using LosBarriosEvents.Authorization; using LosBarriosEvents.Data; using LosBarriosEvents.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; namespace LosBarriosEvents.Pages; [Authorize(Roles = "Administrators,Speakers")] public class SpeakerFormsEditModel : PageModel { private readonly ILogger<SpeakerFormsEditModel> _logger; private readonly ApplicationDbContext _context; private readonly UserManager<LosBarriosUser> _userManager; public SpeakerFormsEditModel(ILogger<SpeakerFormsEditModel> logger, ApplicationDbContext context, UserManager<LosBarriosUser> userManager) { _logger = logger; _context = context; _userManager = userManager; } [BindProperty] public SpeakerForm Form { get; set; } public IActionResult OnGet(int? id) { var userId = _userManager.GetUserId(User); SpeakerForm frm = _context.SpeakerForms.FirstOrDefault(f => f.SpeakerFormId == id); if (frm == null) { return NotFound(); } Form = frm; return Page(); } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } var isAuthorized = User.IsInRole(UserRoles.LosBarriosEventsSpeakerRole) || User.IsInRole(UserRoles.LosBarriosEventsAdministratorRole); if (!isAuthorized) { return Forbid(); } _context.SpeakerForms.Attach(Form).State = EntityState.Modified; await _context.SaveChangesAsync(); return RedirectToPage("./Index"); } }
前端Razor页面(edit.cshtml)
@page @model SpeakerFormsEditModel @{ ViewData["Title"] = "Home page"; } <div class="text-center"> <h1 class="display-4">Edit Speaker Form</h1> </div> <form id="SpeakerEditForm" method="post"> <h2>Edit Form</h2> <hr /> <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div> <div class="form-floating mb-3"> <input asp-for="Form.Title" class="form-control" aria-required="true" placeholder="Lecture Title" /> <label asp-for="Form.Title">Lecture Title</label> <span asp-validation-for="Form.Title" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Form.Description" class="form-control" aria-required="true" placeholder="Description" /> <label asp-for="Form.Description">Description of Lecture</label> <span asp-validation-for="Form.Description" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Form.Duration" class="form-control" aria-required="true" placeholder="Lecture Duration" /> <label asp-for="Form.Duration">Duration</label> <span asp-validation-for="Form.Duration" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Form.SpeakerId" type="hidden"/> </div> <div class="form-floating mb-3"> <input asp-for="Form.Speaker" type="hidden"/> </div> <button id="SpeakerFormSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Submit</button> </form> <a asp-page="./Index">Back to Forms</a>
解决方案
问题根源
表单提交时未传递SpeakerFormId主键值,导致后端绑定的Form对象中SpeakerFormId为默认值0(临时值)。EF Core无法识别这是一个已存在的实体,因此设置状态为Modified时抛出错误。其他页面能正常运行是因为表单包含了主键的隐藏字段。
修复方案1:添加主键隐藏字段
在前端表单中添加SpeakerFormId的隐藏输入框,确保提交时传递主键值:
<div class="form-floating mb-3"> <input asp-for="Form.SpeakerFormId" type="hidden"/> </div>
添加位置可放在SpeakerId隐藏字段附近。
修复方案2:优化后端更新逻辑(更安全)
避免直接使用Attach+Modified的方式,改为从数据库查询原实体后更新属性,这种方式能防止覆盖未提交的字段,且EF Core会自动跟踪实体变化:
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } var isAuthorized = User.IsInRole(UserRoles.LosBarriosEventsSpeakerRole) || User.IsInRole(UserRoles.LosBarriosEventsAdministratorRole); if (!isAuthorized) { return Forbid(); } // 从数据库获取要修改的实体 var existingForm = await _context.SpeakerForms.FindAsync(Form.SpeakerFormId); if (existingForm == null) { return NotFound(); } // 更新需要修改的属性 existingForm.Title = Form.Title; existingForm.Description = Form.Description; existingForm.Duration = Form.Duration; // 若允许修改SpeakerId,可取消下面注释 // existingForm.SpeakerId = Form.SpeakerId; await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }
内容的提问来源于stack exchange,提问作者J D
相关产品推荐
相关产品推荐

