Bug description
We have a navigation property with a list of inherited types, mapped using table-per-hierarchy (TPH). One derived type (LocaleValueAttribute) includes an owned JSON type (LocaleValue). When we try to remove an instance of that type and add a different derived type (StringAttribute) with the same composite key, we run into a NullReferenceException.
I initially placed a comment in #34274, but I now believe it's a separate issue. That's why I created a new ticket.
Your code
using Microsoft.EntityFrameworkCore;
await using var context = new ReproDbContext();
await context.Database.MigrateAsync();
var item = context.Items.Add(new Item
{
Name = "Product 1",
Attributes = [
new LocaleValueAttribute()
{
Key = "TextValue",
Value = new LocaleValue()
{
Entries = [
new LocaleValueEntry()
{
Locale = "en-US",
Value = "Hello"
}
]
}
}
]
});
await context.SaveChangesAsync();
item.Entity.Attributes.RemoveAll(attr => attr.Key == "TextValue");
item.Entity.Attributes.Add(new StringAttribute
{
Key = "TextValue",
Value = "World"
});
await context.SaveChangesAsync(); // Throws the NullReferenceException
// Db context
public class ReproDbContext : DbContext
{
public DbSet<Item> Items { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=efcore-bug-repro;Trusted_Connection=True;MultipleActiveResultSets=true;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>()
.HasKey(x => x.Id);
modelBuilder.Entity<ItemAttribute>(b =>
{
b.HasKey(x => new { x.ItemId, x.Key });
b.HasDiscriminator<string>("Discriminator")
.HasValue<StringAttribute>("string")
.HasValue<LocaleValueAttribute>("locale-value");
});
modelBuilder.Entity<StringAttribute>(b =>
{
b.Property(x => x.Value).HasColumnName("StringValue");
});
modelBuilder.Entity<LocaleValueAttribute>(b =>
{
b.OwnsOne(x => x.Value, bc =>
{
bc.ToJson("LocaleValue");
bc.OwnsMany(x => x.Entries, v =>
{
v.Property(x => x.Locale).IsRequired();
v.Property(x => x.Value);
});
});
});
}
}
// Models
public class Item
{
public int Id { get; set; }
public required string Name { get; set; }
public List<ItemAttribute> Attributes { get; set; } = [];
}
public abstract class ItemAttribute
{
public int ItemId { get; set; }
public required string Key { get; set; }
}
public class StringAttribute : ItemAttribute
{
public string Value { get; set; } = null!;
}
public class LocaleValueAttribute : ItemAttribute
{
public LocaleValue Value { get; set; } = null!;
}
public class LocaleValue
{
public List<LocaleValueEntry> Entries { get; set; } = [];
}
public class LocaleValueEntry
{
public required string Locale { get; set; }
public required string Value { get; set; }
}
Stack traces
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<GenerateColumnModifications>g__FindJsonPartialUpdateInfo|41_2(IUpdateEntry entry, List`1 processedEntries)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<GenerateColumnModifications>g__HandleJson|41_4(List`1 columnModifications, <>c__DisplayClass41_0&)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.GenerateColumnModifications()
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<>c.<get_ColumnModifications>b__33_0(ModificationCommand command)
at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.get_ColumnModifications()
at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.CreateCommandBatches(IEnumerable`1 commandSet, Boolean moreCommandSets, Boolean assertColumnModification, ParameterNameGenerator parameterNameGenerator)+MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IList`1 entries, IUpdateAdapter updateAdapter)+MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChangesAsync(IList`1 entries, 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 Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Work\Experimental\ConsoleApp2\ConsoleApp2\Program.cs:line 40
at Program.<Main>$(String[] args) in C:\Work\Experimental\ConsoleApp2\ConsoleApp2\Program.cs:line 40
at Program.<Main>(String[] args)
Verbose output
EF Core version
9.0.4
Database provider
Microsoft.EntityFrameworkCore.SqlServer
Target framework
.NET 9.0
Operating system
No response
IDE
No response
Bug description
We have a navigation property with a list of inherited types, mapped using table-per-hierarchy (TPH). One derived type (
LocaleValueAttribute) includes an owned JSON type (LocaleValue). When we try to remove an instance of that type and add a different derived type (StringAttribute) with the same composite key, we run into a NullReferenceException.I initially placed a comment in #34274, but I now believe it's a separate issue. That's why I created a new ticket.
Your code
Stack traces
Verbose output
EF Core version
9.0.4
Database provider
Microsoft.EntityFrameworkCore.SqlServer
Target framework
.NET 9.0
Operating system
No response
IDE
No response