dotnet / dotnet/efcore

Fix the exception message for value generated key properties without a value generator

Open
#35,422 7 comments 0 reactions 1 assignee Claimed by @AndriySvyryd View on GitHub
area-change-tracking customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

Previously in EF 8 if you set a property on an entity to an explicit value then it did not matter that there was no value generator available for it.

In EF9 it now throws on Add with the error message

```
The property 'MyEntity.Id' does not have a value set and no value generator is available for properties of type 'int'. Either set a value for the property before adding the entity or configure a value generator for properties of type 'int' in 'OnModelCreating'.
```

Despite the property having a value explicitly set.

### Repro

Minimal repro using InMemoryDatabase with an override to prevent a value generator from being found for integer:

```C#
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
using Microsoft.EntityFrameworkCore.InMemory.ValueGeneration.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.ValueGeneration;

var options = new DbContextOptionsBuilder()
.ReplaceService()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;

var context = new MyContext(options);
var newEntity = new MyEntity { Id = 1, Name = "Test"};
context.Add(newEntity);

class MyContext(DbContextOptions options) : DbContext(options)
{
public DbSet Entities { get; set; }
}

class MyEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}

class IntlessValueGeneratorSelector : InMemoryValueGeneratorSelector
{
public IntlessValueGeneratorSelector(ValueGeneratorSelectorDependencies dependencies, IInMemoryDatabase inMemoryDatabase)
: base(dependencies, inMemoryDatabase)
{
}

public override bool TrySelect(IProperty property, ITypeBase typeBase, out ValueGenerator? valueGenerator)
{
valueGenerator = null;
if (property.ClrType == typeof(int)) return false;
return base.TrySelect(property, typeBase, out valueGenerator);
}

}
```

### Stack traces

```
Unhandled exception. System.NotSupportedException: The property 'MyEntity.Id' does not have a value set and no value generator is available for properties of type 'int'. Either set a value for the property before adding the entity or configure a value generator for properties of type 'int' in 'OnModelCreating'.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ValueGenerationManager.CheckPropertyWithNoGenerator(IProperty property)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ValueGenerationManager.Generate(InternalEntityEntry entry, Boolean includePrimaryKey)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey, Nullable`1 fallbackState)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
at Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)
at Microsoft.EntityFrameworkCore.DbContext.Add[TEntity](TEntity entity)
at Program.$(String[] args) in E:\src\temp\SetGeneratedRepro\SetGeneratedRepro\Program.cs:line 16
```

### Povider and version information

EF Core version: 9.0.0
Database provider: All
Target framework: .NET 8.0
Operating system: Windows
IDE: Rider 2404.3.3

### Additional notes

I believe this was unintentionally introduced in https://github.com/dotnet/efcore/commit/838ae11d28c06d2a31114ce6b51015f702e26b83#diff-b9fdb780a381b36843749313b829bf2164ad758390d555891f75f05c0ce2c365R215 and was not caught in tests because all the built-in providers now provide a value generator for integers.

Specifically EF9 is now only checking entry.HasExplicitCheck after it has already decided that the property is problematic in TryFindValueGenerator.

### Possible fix

It's possible that the HasExplicitCheck needs to happen before the GetContainingKeysCheck although I'm not entirely sure what that block is trying to achieve.

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.