Bug description
I have just upgraded a large project to .NET 9. All seems to work well, except for our test suite that works on the InMemory provider. For all entities that have a non-nullable enum, an exception is thrown when an entity is added DbSet.Add(). It does not matter which entity or which DbSet, as long as it is an enum, and that it is required (non-nullable). This is where the exception is thrown:


As can be seen, the TypeMapper.Converter for my enum is null, but the forced null forgiving ! makes me believe it was very unexpected for it to be null at this point.

I have created a minimal repro to try to get the same error in a freshly created .NET 9 project, but it does not (of course) crash there. This leads me to believe something I've done wrong upgrading the .NET 8 project to .NET 9. Please help me point out how I can debug this. I have invested in thousands of InMemory tests and would be sad to have to let them go.
Workaround: If I change my enum to be nullable, the test will run successfully. So it is only for non-nullable enum properties it fails.
Your code
This repro works fine, but demonstrates what is not working in my real project.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
await using var ctx = new BlogContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
ctx.BillingGroups.Add(new BillingGroup() { Plan = BillingPlan.Personal });
public class BlogContext : DbContext
{
public DbSet<BillingGroup> BillingGroups { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}
public enum BillingPlan : int
{
Personal = 1,
Business = 2
}
public class BillingGroup
{
public int Id { get; set; }
public required BillingPlan Plan { get; set; }
}
Main differences from real project;
- BillingGroup is of course a much more complex entity
- I am using migrations
- It is run from a test method from a separate test project
Stack traces
> Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.RuntimeProperty.Sentinel.get() Line 341 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Internal.ExpressionExtensions.MakeHasSentinel(System.Linq.Expressions.Expression currentValueExpression, Microsoft.EntityFrameworkCore.Metadata.IReadOnlyPropertyBase propertyBase) Line 27 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyAccessorsFactory.CreateCurrentValueGetter<TrastxWebApp.Blueprints.Models.BillingPlan>(Microsoft.EntityFrameworkCore.Metadata.IPropertyBase propertyBase, bool useStoreGeneratedValues) Line 147 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyAccessorsFactory.CreateExpressions<TrastxWebApp.Blueprints.Models.BillingPlan>(Microsoft.EntityFrameworkCore.Metadata.IPropertyBase propertyBase, out System.Linq.Expressions.Expression<System.Func<Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry, TrastxWebApp.Blueprints.Models.BillingPlan>> currentValueGetter, out System.Linq.Expressions.Expression<System.Func<Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry, TrastxWebApp.Blueprints.Models.BillingPlan>> preStoreGeneratedCurrentValueGetter, out System.Linq.Expressions.Expression<System.Func<Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry, TrastxWebApp.Blueprints.Models.BillingPlan>> originalValueGetter, out System.Linq.Expressions.Expression<System.Func<Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry, TrastxWebApp.Blueprints.Models.BillingPlan>> relationshipSnapshotGetter, out System.Linq.Expressions.Expression<System.Func<Microsoft.EntityFrameworkCore.Storage.ValueBuffer, object>> valueBufferGetter) Line 109 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyAccessorsFactory.CreateGeneric<TrastxWebApp.Blueprints.Models.BillingPlan>(Microsoft.EntityFrameworkCore.Metadata.IPropertyBase propertyBase) Line 48 C#
[Lightweight Function]
System.Private.CoreLib.dll!System.Reflection.MethodBaseInvoker.InvokeWithOneArg(object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, object[] parameters, System.Globalization.CultureInfo culture) Line 95 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyAccessorsFactory.Create(Microsoft.EntityFrameworkCore.Metadata.IPropertyBase propertyBase) Line 38 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase.Microsoft.EntityFrameworkCore.Metadata.Internal.IRuntimePropertyBase.get_Accessors.AnonymousMethod__36_0(Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase property) Line 198 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized<Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase, Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyAccessors>(ref Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyAccessors target, Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase param, System.Func<Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase, Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyAccessors> valueFactory) Line 25 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase.Microsoft.EntityFrameworkCore.Metadata.Internal.IRuntimePropertyBase.Accessors.get() Line 196 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyBaseExtensions.GetPropertyAccessors(Microsoft.EntityFrameworkCore.Metadata.IPropertyBase propertyBase) Line 66 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.GetCurrentValue<TrastxWebApp.Blueprints.Models.BillingPlan>(Microsoft.EntityFrameworkCore.Metadata.IPropertyBase propertyBase) Line 976 C#
[Lightweight Function]
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.OriginalValues.OriginalValues(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry entry) Line 12 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.EnsureOriginalValues() Line 1206 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntrySubscriber.SnapshotAndSubscribe(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry entry) Line 31 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry entry) Line 583 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(Microsoft.EntityFrameworkCore.EntityState oldState, Microsoft.EntityFrameworkCore.EntityState newState, bool acceptChanges, bool modifyProperties) Line 400 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(Microsoft.EntityFrameworkCore.EntityState entityState, bool acceptChanges, bool modifyProperties, Microsoft.EntityFrameworkCore.EntityState? forceStateWhenUnknownKey, Microsoft.EntityFrameworkCore.EntityState? fallbackState) Line 205 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntryGraphNode<(Microsoft.EntityFrameworkCore.EntityState TargetState, Microsoft.EntityFrameworkCore.EntityState StoreGenTargetState, bool Force)> node) Line 123 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<(Microsoft.EntityFrameworkCore.EntityState, Microsoft.EntityFrameworkCore.EntityState, bool)>(Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntryGraphNode<(Microsoft.EntityFrameworkCore.EntityState, Microsoft.EntityFrameworkCore.EntityState, bool)> node, System.Func<Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntryGraphNode<(Microsoft.EntityFrameworkCore.EntityState, Microsoft.EntityFrameworkCore.EntityState, bool)>, bool> handleNode) Line 26 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry rootEntry, Microsoft.EntityFrameworkCore.EntityState targetState, Microsoft.EntityFrameworkCore.EntityState storeGeneratedWithKeySetTargetState, bool forceStateWhenUnknownKey) Line 43 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Internal.InternalDbSet<System.__Canon>.SetEntityState(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry entry, Microsoft.EntityFrameworkCore.EntityState entityState) Line 581 C#
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Internal.InternalDbSet<TrastxWebApp.Blueprints.Models.BillingGroup>.Add(TrastxWebApp.Blueprints.Models.BillingGroup entity) Line 193 C#
TrastxWebApp.Tests.dll!TrastxWebApp.Tests.Extensions.VerificationExtensionsShould.FilterQueryableCorrectly() Line 220 C#
[Lightweight Function]
System.Private.CoreLib.dll!System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(object obj, System.Reflection.BindingFlags invokeAttr) Line 57 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestInvoker<System.__Canon>.CallTestMethod(object testClassInstance) Line 149 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestInvoker<Xunit.Sdk.IXunitTestCase>.InvokeTestMethodAsync.AnonymousMethod__1() Line 256 C#
xunit.execution.dotnet.dll!Xunit.Sdk.ExecutionTimer.AggregateAsync(System.Func<System.Threading.Tasks.Task> asyncAction) Line 48 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestInvoker<System.__Canon>.InvokeTestMethodAsync.AnonymousMethod__0() Line 215 C#
xunit.core.dll!Xunit.Sdk.ExceptionAggregator.RunAsync(System.Func<System.Threading.Tasks.Task> code) Line 90 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestInvoker<Xunit.Sdk.IXunitTestCase>.InvokeTestMethodAsync(object testClassInstance) Line 214 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestInvoker.InvokeTestMethodAsync(object testClassInstance) Line 112 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestInvoker<Xunit.Sdk.IXunitTestCase>.RunAsync.AnonymousMethod__46_0() Line 180 C#
xunit.core.dll!Xunit.Sdk.ExceptionAggregator.RunAsync<decimal>(System.Func<System.Threading.Tasks.Task<decimal>> code) Line 107 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestInvoker<System.__Canon>.RunAsync() Line 163 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestRunner.InvokeTestMethodAsync(Xunit.Sdk.ExceptionAggregator aggregator) Line 88 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestRunner.InvokeTestAsync(Xunit.Sdk.ExceptionAggregator aggregator) Line 70 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestRunner<System.__Canon>.RunAsync.AnonymousMethod__0() Line 149 C#
xunit.core.dll!Xunit.Sdk.ExceptionAggregator.RunAsync<System.Tuple<decimal, string>>(System.Func<System.Threading.Tasks.Task<System.Tuple<decimal, string>>> code) Line 107 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestRunner<Xunit.Sdk.IXunitTestCase>.RunAsync() Line 149 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestCaseRunner.RunTestAsync() Line 140 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestCaseRunner<Xunit.Sdk.IXunitTestCase>.RunAsync() Line 82 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestCase.RunAsync(Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, object[] constructorArguments, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) Line 170 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestMethodRunner.RunTestCaseAsync(Xunit.Sdk.IXunitTestCase testCase) Line 45 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestMethodRunner<Xunit.Sdk.IXunitTestCase>.RunTestCasesAsync() Line 136 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestMethodRunner<Xunit.Sdk.IXunitTestCase>.RunAsync() Line 106 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestClassRunner.RunTestMethodAsync(Xunit.Abstractions.ITestMethod testMethod, Xunit.Abstractions.IReflectionMethodInfo method, System.Collections.Generic.IEnumerable<Xunit.Sdk.IXunitTestCase> testCases, object[] constructorArguments) Line 206 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestClassRunner<Xunit.Sdk.IXunitTestCase>.RunTestMethodsAsync() Line 233 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestClassRunner<Xunit.Sdk.IXunitTestCase>.RunAsync() Line 175 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestCollectionRunner.RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo class, System.Collections.Generic.IEnumerable<Xunit.Sdk.IXunitTestCase> testCases) Line 185 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestCollectionRunner<Xunit.Sdk.IXunitTestCase>.RunTestClassesAsync() Line 136 C#
xunit.execution.dotnet.dll!Xunit.Sdk.TestCollectionRunner<Xunit.Sdk.IXunitTestCase>.RunAsync() Line 101 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestAssemblyRunner.RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable<Xunit.Sdk.IXunitTestCase> testCases, System.Threading.CancellationTokenSource cancellationTokenSource) Line 346 C#
xunit.execution.dotnet.dll!Xunit.Sdk.XunitTestAssemblyRunner.RunTestCollectionsAsync.AnonymousMethod__2() Line 250 C#
System.Private.CoreLib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.Task<Xunit.Sdk.RunSummary>>.InnerInvoke() Line 490 C#
System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread threadPoolThread, System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 264 C#
System.Private.CoreLib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot, System.Threading.Thread threadPoolThread) Line 2346 C#
System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Line 1120 C#
System.Private.CoreLib.dll!System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart() Line 128 C#
Verbose output
EF Core version
9.0.1
Database provider
InMemory
Target framework
.NET 9
Operating system
No response
IDE
No response
Bug description
I have just upgraded a large project to .NET 9. All seems to work well, except for our test suite that works on the InMemory provider. For all entities that have a non-nullable enum, an exception is thrown when an entity is added DbSet.Add(). It does not matter which entity or which DbSet, as long as it is an enum, and that it is required (non-nullable). This is where the exception is thrown:
As can be seen, the TypeMapper.Converter for my enum is null, but the forced null forgiving ! makes me believe it was very unexpected for it to be null at this point.
I have created a minimal repro to try to get the same error in a freshly created .NET 9 project, but it does not (of course) crash there. This leads me to believe something I've done wrong upgrading the .NET 8 project to .NET 9. Please help me point out how I can debug this. I have invested in thousands of InMemory tests and would be sad to have to let them go.
Workaround: If I change my enum to be nullable, the test will run successfully. So it is only for non-nullable enum properties it fails.
Your code
This repro works fine, but demonstrates what is not working in my real project.
Main differences from real project;
Stack traces
Verbose output
EF Core version
9.0.1
Database provider
InMemory
Target framework
.NET 9
Operating system
No response
IDE
No response