我尝试从DB表中获取模型,对其进行更改,然后在回发表单时更新所做的更改。我一直在挖掘通过多种资源试图找到这个问题的解决方案,似乎不能理解到底是怎么回事,或找到一个很好的解决方案。
从以前的讲座和经验,我已经得到了这个工作使用
_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”绑定为FK:
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; }
}
}
编辑.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");
}
}
edit.cshtml
绑定到Razorpage并在发布时回调
@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>
我尝试了几乎所有我能找到的stackoverflow,microsoft和github帖子的变体,发现自己试图向墙上扔东西,直到它粘上几个小时。没有运气,我决定最终尝试并在这里发布。对我来说奇怪的是,我在另一个模型的另一个编辑页面上几乎完全复制了这段代码,并且能够很好地工作。我觉得这是如此微不足道的事情,但我一直在疯狂地试图弄清楚这一点。我还通过迁移来验证它是正确构建的,据我所知没有发现任何错误。任何帮助和提前谢谢你
1条答案
按热度按时间jaql4c8m1#
只是有一个官方的答案,所有感谢伊万Stoev以上,他提到:
错误消息指示调用Attach时SpeakerFormId为0(零,int的默认值),因此EFC将其视为新值并生成临时值。原因是什么,模型绑定不是我的领域,但我猜您需要在razorform中隐藏输入来保存接收到的Form.SpeakerFormId并将其与其他属性沿着发送回去
我错误的假设是,在制作cshtml页面时,我只需要添加任何我想要更改/编辑的属性。这是不正确的,我一直收到错误的原因是因为我假设主键是自动从原始查询对象中结转的。这是不是这样的,因为我需要添加隐藏属性到cshtml页面,以便它会像这样传递回来:
总而言之,确保列出了完整的对象并将其绑定到前端(cshtml),以便将整个对象传递回去。包括主键、外键、对象导航属性(EFCore)和其他任何内容。
虽然我确信有一种方法可以只在前端使用某些属性,并在后端填充其余的属性,但对于我编程的方式,这是工作的方式。