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 并假设此任务将在我的方法返回时继续吗?

用户 DLL/EXE 中的堆分配失败

sql数据库分配用户dbo权限失败

如何在 Web API 控制器中使用分层架构处理服务器错误