I have an abstract AuditObject
class with audit fields I can set automatically before saving changes on my entities.
public abstract class AuditObject
{
public string CreatedBy { get; set; }
public string UpdatedBy { get; set; }
}
In my DbContext before saving I set the audit fields:
private void OnBeforeSaving()
{
var user = _someService.GetUserName();
foreach (var entry in ChangeTracker.Entries())
{
if (entry.State == EntityState.Modified && entry.Entity is AuditObject auditObject)
{
auditObject.UpdatedBy = user;
entry.Property("CreatedBy").IsModified = false;
}
else if (entry.State == EntityState.Added && entry.Entity is AuditObject auditObject)
{
auditObject.CreatedBy = user;
auditObject.UpdatedBy = user;
}
}
}
I inherit that on an entity I have with related entities as well:
public abstract class Order: AuditObject
{
public long Id { get; set; }
public List<OrderProducts> Products { get; set; }
}
public abstract class OrderProducts: AuditObject
{
public long Id { get; set; }
public string Name { get; set; }
}
Now when I receive the updated Order on my endpoint I set the Products list to the updated list.
[HttpPut("{id:long}")]
public async Task<Order> UpdateOrder(long id, Order order)
{
var currentOrder = await _dbContext.Order
.Include(x => x.Products)
.FirstOrDefaultAsync(x => x.Id == order.Id);
currentOrder.Products = order.Products;
await _dbContext.SaveChangesAsync();
return Ok(currentOrder);
}
When I debug through the change tracker entries Entity Framework believes there was a modified and deleted state and results in creating new records for the Products. So if I have an existing Order with 1 product with Id 1, the first change tracker entry it sees that OrderProduct 1 was modified, and sets the UpdatedBy and flags the CreatedBy as not modified. Then it has a change tracker entry of key 1 being deleted. The end result is I get an exception:
System.InvalidOperationException: The property
‘OrderProduct.CreatedBy’ cannot be
assigned a value generated by the database. Store-generated values can
only be assigned to properties configured to use store-generated
values.
On initial creation there is never an issue, the CreatedBy and UpdatedBy get set and a new OrderProduct record is created. It is only when I am updating the list of products with existing product Ids. Is there a way I can tell Entity Framework when assigning the list of products to not consider an update both a modify and delete?