SaveChangesAsync 失败,分配用户到对象 AspNetUsers 重复键违规
Posted
技术标签:
【中文标题】SaveChangesAsync 失败,分配用户到对象 AspNetUsers 重复键违规【英文标题】:SaveChangesAsync fails with assigned users to object AspNetUsers duplicate key violation 【发布时间】:2022-01-06 04:27:50 【问题描述】:我正在尝试创建一个项目可以有多个用户的项目项。您可以通过输入用户的姓名并按下按钮来分配用户;控制器将找到用户并返回用户(如果存在)。然后将它们添加到列表中,然后在项目创建提交时将其分配给项目。当项目使用 POST 时,SaveChangesAsync() 会产生一个带有错误的 http 响应 500:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'PK_AspNetUsers'. Cannot insert duplicate key in object 'dbo.AspNetUsers'. The duplicate key value is (fa2f3cdf-6147-4973-9c1e-988a21408610).
The statement has been terminated.
我似乎无法弄清楚为什么会发生这种情况,我觉得表单被提交了两次,或者通过两个按钮错误地提交了 Editform。我对 Blazor 和 EF 非常陌生,因此我希望能深入了解可能导致此问题的原因以及如何解决此问题。提前致谢
创建剃须刀页面
<EditForm Model="@projectItem" OnValidSubmit="@CreateNewProject">
<DataAnnotationsValidator />
<div class="form-group">
<label class="custom-control-label"> Name </label>
<InputText id="title" @bind-Value="projectItem.ProjectName" class="form-control" />
<InputText id="title" @bind-Value="projectItem.ProjectDescription" class="form-control" />
<ValidationMessage For="@(() => projectItem.ProjectName)" />
<button type="submit" class="btn btn-primary">Create Project</button>
</div>
<ValidationSummary />
</EditForm>
<EditForm Model="@AssignUser" OnValidSubmit="@AddUserToProject">
<DataAnnotationsValidator />
<div class="form-group">
<label class="custom-control-label"> Assign users </label>
<InputText id="title" type="email" @bind-Value="@AssignUser.UserName" class="form-control" />
<button type="submit" class="btn btn-primary">Add User</button>
</div>
</EditForm>
<p> Currently assigned users </p>
@if (ListOfAllUsers.Count() == 0)
<p> No users assigned yet</p>
else
@for(int i = 0; i < ListOfAllUsers.Count(); i++)
<li> @ListOfAllUsers[i].UserName </li>
private Project projectItem get; set; = new Project();
private ApplicationUser AssignUser get; set; = new ApplicationUser();
private List<ApplicationUser> ListOfAllUsers get; set; = new List<ApplicationUser>();
//submit and create project with the assigned users
private async void CreateNewProject()
projectItem.AssignedUsersToProject = ListOfAllUsers;
try
var response = await Http.PostAsJsonAsync("Projects", projectItem);
var content = await response.Content.ReadAsStringAsync();
var project = JsonConvert.DeserializeObject<Project>(content);
if(response.IsSuccessStatusCode)
Navigation.NavigateTo($"/projects");
catch (Exception e)
// find and add user
private async void AddUserToProject()
try
var response = await Http.PostAsJsonAsync($"Projects/AssignUser.UserName", AssignUser);
var content = await response.Content.ReadAsStringAsync();
var userobject = JsonConvert.DeserializeObject<ApplicationUser>(content);
if (response.IsSuccessStatusCode)
if (!ListOfAllUsers.Any(n => n.UserName.Equals(userobject.UserName)))
ListOfAllUsers.Add(userobject);
else
AssignUser.UserName = "This user is already added to the project.";
else if (!response.IsSuccessStatusCode)
AssignUser.UserName = "User not found, try again";
catch (Exception e)
控制器:创建项目
[HttpPost]
public async Task<ActionResult<Project>> PostProject(Project project)
_context.Projects.Add(project);
await _context.SaveChangesAsync(); // error occurs when reaching this point
return CreatedAtAction("GetProject", new id = project.ProjectId , project);
控制器:查找并返回用户
[HttpPost("userEmail")]
public async Task<ActionResult<ApplicationUser>> AddUserToProject(string userEmail)
try
// Find user
ApplicationUser userValid = _userManager.Users.Where
(s => s.Email == userEmail).First();
return userValid;
catch (InvalidOperationException)
return BadRequest();
型号
public class Project
[Key]
public Guid ProjectId get; set;
[Required]
public string ProjectName get; set;
[JsonIgnore]
public ICollection<ApplicationUser> AssignedUsersToProject get; set;
public Company assignedCompanyForProject get; set;
public class ApplicationUser : IdentityUser
public string firstName get; set;
public string lastName get; set;
[JsonIgnore]
public ICollection<Project> Projects get; set;
表格图片
您键入要添加到项目中的用户的名称,然后单击“添加用户”,如果用户是存在的用户,它将查找并返回用户(这不会创建项目,但只会查找用户)。然后将该用户添加到 ListOfAllUsers 列表中,然后循环显示所有当前分配的用户。一旦在相关用户旁边给出了项目标题和描述,应按下“创建项目”按钮,这将使用已确定的分配用户创建整个项目。这是提交到数据库以添加新项目项的内容
建议的更改
实现 EntityState.Unchanged;
foreach(var x in project.AssignedUsersToProject)
_context.Entry(x).State = EntityState.Unchanged;
_context.Projects.Add(project);
导致以下错误:
ystem.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32
【问题讨论】:
如果在现有项目中添加一个新用户,它基本上是用新用户创建一个全新的项目吗?因为您似乎只使用.Add
。或者这是否会使用新用户更新现有项目?我的猜测是您正在尝试添加您拥有现有用户的现有项目的副本。如果您尝试添加与该用户相同的项目,它会抱怨有 2 个用户使用相同的密钥。因此,您要么只更新项目并忽略现有用户。或者您必须完全覆盖/删除当前项目,然后注入新项目。
表单的工作方式是您输入要添加的用户名,然后将用户添加到列表中。您可以不断重复此过程来添加用户。要与您选择的这些用户确认项目创建,请单击“创建项目”按钮,该按钮应与相关用户一起提交新项目项。我将添加表单的图像,这可能更容易可视化
@HenkHolterman 在上述多对多关系的情况下,如果我将子对象的状态设置为 Detached,这会破坏 EF 自动生成的第三个表关系吗?
@HenkHolterman 我已经进行了您建议的更改,但是由于这样做,我似乎遇到了另一个错误。我已经更新了底部的帖子以显示我实现的代码和我得到的错误
感谢@HenkHolterman 的帮助
【参考方案1】:
在创建项目时在控制器中实现以下解决了该问题。
foreach(var x in project.AssignedUsersToProject)
_context.Entry(x).State = EntityState.Unchanged;
_context.Projects.Add(project);
【讨论】:
以上是关于SaveChangesAsync 失败,分配用户到对象 AspNetUsers 重复键违规的主要内容,如果未能解决你的问题,请参考以下文章
实体框架 6、.NET Framework 4.0 和 SaveChangesAsync
我可以在我的 Web API 中调用 DBContext.SaveChangesAsync 并假设此任务将在我的方法返回时继续吗?