Search code examples
asp.net-coreentity-framework-coreasp.net-identityblazor

SaveChangesAsync fails with assigned users to object AspNetUsers duplicate key violation


I am trying to create a Project item where a project can have multiple users. You assign the user by typing their name and pressing the button; the controller will find the user and return the user if they exist. They are then added to the List which is then assigned to the project on project creation submit. When the POST is engaged for the project the SaveChangesAsync() produces a http response 500 with the error:

    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.

I cannot seem to figure out why this might be happening, I feel the form is being submitted twice or the Editform is incorrectly being submitted via the two buttons. I very new to Blazor and EF so I would appreciate any insight into what might be causing this and how I might go about resolving this issue. Thanks in advance

Create razor page

<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)
    {

    }
}

Controller: Create Project

[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);

    }

Controller: find and return user

[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();
        }
    }

Models

 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; }
}

Form image

enter image description here You type the name of the user you want to add to the project and click "Add User" which will find and return the user if they are a user that exists (This does not create the project yet only finds user). This user is then added to the ListOfAllUsers list which is then looped through to display all the current users assigned. Once a project title and description is given alongside the relevant users the "Create Project" button should be pressed which will create the whole project with the assigned users which have been determined. This is what is submitted to the database to add a new Project item

Suggested changes

Implementing EntityState.Unchanged;

foreach(var x in project.AssignedUsersToProject)
        {
            _context.Entry(x).State = EntityState.Unchanged;
        }
            
        _context.Projects.Add(project);

Causing following error:

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

Solution

  • Implementing the following in the controller when creating the project solved the issue.

    foreach(var x in project.AssignedUsersToProject)
        {
            _context.Entry(x).State = EntityState.Unchanged;
        }
            
        _context.Projects.Add(project);