I am using ASP.NET Core 5 Web API and I am trying to use the new C# records as my model classes. But I am getting an EF Core error about tracking problems whenever I update my modified model using the with
expression:
System.InvalidOperationException: The instance of entity type 'Product' cannot be
tracked because another instance with the key value '{ID: 2}' is already being
tracked. When attaching existing entities, ensure that only one entity instance
with a given key value is attached.
I believe this is due to how "mutating" records creates a new object instance and EF Core's tracking system doesn't like that, but I'm not sure the best way to fix it. Does anyone have any recommendations? Or should I go back to using regular classes instead of records?
Here's a snippet to reproduce the problem:
// Models/Product.cs
public record Product(int ID, string Name);
// Controllers/ProductController.cs
[HttpGet("test/{id}")]
public async Task<Product> ExampleControllerAction(int id, CancellationToken cancellationToken)
{
string newName = "test new name!!";
Product product = await db.Products.FindAsync(new object[] { id }, cancellationToken);
product = product with { Name = newName }; // Modify the model.
db.Update(product); // InvalidOperationException happens here.
await db.SaveChangesAsync(cancellationToken);
return product;
}
From official document
Entity Framework Core depends on reference equality to ensure that it uses only one instance of an entity type for what is conceptually one entity. For this reason, record types aren't appropriate for use as entity types in Entity Framework Core.
This could confuse some people. Pay close attention to the documentation. record
s might not be suited for entities, but they are just fine for owned types for example, like value objects (in DDD-terms), because such values don't have a conceptual unique identity.
For example, if an Address entity, modeled as a class, owns a City value object, modeled as a record, EF would usually map City as columns inside Address like this: City_Name
, City_Code
, etc. (ie. record name joined with an underscore and the property name).
Notice that City has no Id, because we're not tracking unique cities here, just names and codes and whatever other information you could add to a City.
Whatever you do, don't add Ids to records and try to map them manually, because two records with the same Id don't necessarily mean the same conceptual entity to EF, probably because EF compares object instances by reference (records are ref types) and doesn't use the standard equality comparer which is different for records than for standard objects (needs confirmation).
I myself don't know how EF's works on the inside very well to talk with more certainty, but I trust the docs and you should probably trust them too, unless you want to read the source code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With