dotnet / dotnet/efcore

Allow detection of state for entities with generated keys to be switched off

Open
#33,560 3 comments 0 reactions 0 assignees View on GitHub
area-change-tracking customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

I have a two models (parent, child) with GUID as PrimaryKey. In a one-to-many relation. By default, the option "ValueGeneratedOnAdd" is activated for PrimaryKey as described here:
https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations#primary-keys

Now I would like to add child-models which have already a primary key set by the client application.
I add the child-models to an list of childs in the parent-model which already exists in database and is loaded before.

In that case the context tracks this child-entites as modified also described here (information card):
https://learn.microsoft.com/en-us/ef/core/modeling/keys?tabs=data-annotations#key-types-and-values

Doesn't Work:
```cs
public async Task GetDemo()
{
var parent = await context.Parents.FindAsync(Guid.Parse("0b2e2dc0-fd67-11ee-8fec-84a93832722b"));
// child has a primary key set
var child = new Child("child1", Guid.NewGuid());

parent.Children.Add(child);

// EntityEntry (child) state is: modified
await context.SaveChangesAsync();

return Ok();
}
```

Works:
```cs
public async Task GetDemo()
{
var parent = await context.Parents.FindAsync(Guid.Parse("0b2e2dc0-fd67-11ee-8fec-84a93832722b"));
// child has none primary key set
var child = new Child("child1");

parent.Children.Add(child);

// EntityEntry (child) state is: added
await context.SaveChangesAsync();

return Ok();
}
```

On SaveChanges I get the following exception:
`Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded.`
```
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithRowsAffectedOnlyAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeAsync(RelationalDataReader reader, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
```

If I follow the approche, described here:
https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations#overriding-value-generation
```cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity().Property(x => x.PrimaryKey)
.ValueGeneratedOnAddOrUpdate()
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
}
```
This cannot be configured for properties which are configured as primary key.

How can I configure to use provided primary or generate if null?

EF Core version: 8.0.2
Database provider: Pomelo.EntityFrameworkCore.MySql
Target framework: NET 8.0

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.