Paul Meems
Paul Meems

Reputation: 3284

Violation of PRIMARY KEY constraint in Entity Framework Core

I have a .NET5 Blazor Server application using EFCore5. I have two entities that have a many-to-many relationship with each other. Let's call them 'Books' and 'Tags'.
EFCore provides the BookTag join table.

On my page, I have a form that is populated with the book details and has a multi select box for the tags. I include the tags when I get the book: context.Books.Include(x=>x.Tags); In debug mode, I see the Book item has several tags. But when I update some details of the book and save the form, calling context.Books.Update(book); I get an error because of duplicate keys in the BookTag join table.
It seems EFCore tries to insert entries in that table, but it should do a merge or insert/update.

I've read numerous articles about the many-to-many but all of them just show how to retrieve data. I can't find an example of how to update.

Edit:
I'm using the GenericRepository as explained by @carl-franklin in his excellent BlazorTrain series.

Edit2:
Here is some relevant code, I'm now not using the GenericRepo:
Startup.cs
services.AddDbContextFactory<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DbConnection")));

ApplicationDbContext.cs has only the DbSets.

Book.cs

[Key] 
public string Code { get; set; }
public string Name { get; set; } = "";
public virtual ICollection<Tag> TagCollection { get; set; }

Tag.cs

[Key] 
public string Code { get; set; }
public string Name { get; set; } = "";
public virtual ICollection<Book> BookCollection { get; set; }

Book.razor.cs

[Inject] private IDbContextFactory<ApplicationDbContext> contextFactory { get; set; }
private ApplicationDbContext context { get; set; }
protected IEnumerable<Book> BookList;

protected override async Task OnInitializedAsync()
{
    context = contextFactory.CreateDbContext();
    context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
    
    BookList = await context.Books.Include(x => x.TagCollection).ToListAsync();
}

private async Task UpdateBook(Book book)
{
    // Called after submitting form
    context.Books.Update(book);
    await context.SaveChangesAsync();
}

Even if I don't change anything, just update I get the duplicate 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_BookTag'. Cannot insert duplicate key in object 'dbo.BookTag'

In UpdateBook() I see the book has tags, as expected. It looks like EFCore is inserting them in the join table without checking if they already exist.

Upvotes: 0

Views: 3809

Answers (1)

Robert Harvey
Robert Harvey

Reputation: 180788

Change tracking must be turned on for this to work. Otherwise, you have to track the changes yourself; Entity Framework thinks you're adding a new record, but the key is the same as an existing record, which is why you're getting the error.

Upvotes: 1

Related Questions