Bug description
GetDatabaseValues does not return the values of complex collection properties. Trying to access the value of the property via the indexer always returns null. Inspecting the returned ArrayPropertyValues object in the debugger, shows a _complexCollectionValues field with one, null sub-list inside of it.
This came up in the context of a feature request for Audit.NET where it was discovered/noted that GetDatabaseValues does not return the values of complex collections.
Honestly I am not sure if this is a bug or entirely expected/unimplemented. I spent a while searching around the issues but did not find anything that seemed to match. It might be related to #38486 but again, I am not certain
Your code
Project File
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Npgsql" Version="10.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.11" />
</ItemGroup>
</Project>
Entities and Context
public sealed class TestContext(DbContextOptions<TestContext> opts) : DbContext(opts)
{
public DbSet<Test> Things => Set<Test>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Test>(e =>
{
e.ToTable("things");
e.HasKey(p => p.Id);
e.ComplexCollection(p => p.Items, p =>
{
p.ToJson();
p.IsRequired();
p.Property(c => c.Title).IsRequired();
});
});
}
}
public sealed class Test
{
public int Id { get; set; }
public List<ComplexItem> Items { get; set; } = null!;
}
public class ComplexItem
{
public string Title { get; set; } = null!;
}
Using the following Program:
Program
using System.Collections;
using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
const string pgconn = "Host=localhost;Port=5432;Database=repro;Username=postgres;Password=postgres";
var opts = new DbContextOptionsBuilder<TestContext>()
.UseNpgsql(pgconn, s => s.SetPostgresVersion(new Version(17, 0)))
.Options;
await using (var ctx = new TestContext(opts))
{
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
var newItem = new Test
{
Id = 1, Items = [ new ComplexItem { Title = "Title1" } ]
};
ctx.Things.Add(newItem);
await ctx.SaveChangesAsync();
var entry = ctx.Entry(newItem);
var origVal = entry.OriginalValues[nameof(Test.Items)];
var currVal = entry.CurrentValues[nameof(Test.Items)];
Console.WriteLine("After Initial Save Change");
Console.WriteLine("Original Items.Count: {0}", GetCount(origVal));
Console.WriteLine("Current Items.Count: {0}", GetCount(currVal));
Console.WriteLine();
}
await using (var ctx = new TestContext(opts))
{
var thing = await ctx.Things.SingleAsync();
thing.Items.Add(new ComplexItem { Title = "TestAdd" });
var entry = ctx.Entry(thing);
var origVal = entry.OriginalValues[nameof(Test.Items)];
var currVal = entry.CurrentValues[nameof(Test.Items)];
Console.WriteLine("After Local Modification");
Console.WriteLine("Original Items.Count: {0}", GetCount(origVal));
Console.WriteLine("Current Items.Count: {0}", GetCount(currVal));
var dbValues = (await entry.GetDatabaseValuesAsync())!;
var dbVal = dbValues[nameof(Test.Items)];
Console.WriteLine("DB Items.Count: {0}", GetCount(dbVal));
Console.WriteLine();
Console.WriteLine("===DEBUG===");
var complexCollectionValues = GetCollection((ArrayPropertyValues)dbValues);
Console.WriteLine("DB _complexCollectionValues.Count: {0}", complexCollectionValues.Length);
for (var i = 0; i < complexCollectionValues.Length; ++i)
Console.WriteLine("DB _complexCollectionValues[{0}].Count={1}", i, GetCount(complexCollectionValues[i]));
}
static string GetCount(object? o) => o is IList l ? l.Count.ToString() : "<null>";
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_complexCollectionValues")]
static extern ref List<ArrayPropertyValues?>?[] GetCollection(ArrayPropertyValues obj);
Verbose output
The attached program produces the following output:
After Initial Save Change
Original Items.Count: 1
Current Items.Count: 1
After Local Modification
Original Items.Count: 1
Current Items.Count: 2
DB Items.Count: <null>
===DEBUG===
DB _complexCollectionValues.Count: 1
DB _complexCollectionValues[0].Count=<null>
That said, I would expect the following:
After Initial Save Change
Original Items.Count: 1
Current Items.Count: 1
After Local Modification
Original Items.Count: 1
Current Items.Count: 2
DB Items.Count: 1 <----- non-null!
===DEBUG===
DB _complexCollectionValues.Count: 1
DB _complexCollectionValues[0].Count=1 <---- non-null
EF Core version
10.0.11
Database provider
Npgsql.EntityFrameworkCore.PostgreSQL (but I don't think it matters)
Target framework
.NET 10
Operating system
Windows 11
IDE
Rider, VS 2026
Bug description
GetDatabaseValuesdoes not return the values of complex collection properties. Trying to access the value of the property via the indexer always returnsnull. Inspecting the returnedArrayPropertyValuesobject in the debugger, shows a_complexCollectionValuesfield with one,nullsub-list inside of it.This came up in the context of a feature request for Audit.NET where it was discovered/noted that
GetDatabaseValuesdoes not return the values of complex collections.Honestly I am not sure if this is a bug or entirely expected/unimplemented. I spent a while searching around the issues but did not find anything that seemed to match. It might be related to #38486 but again, I am not certain
Your code
Project File
Entities and Context
Using the following Program:
Program
Verbose output
The attached program produces the following output:
That said, I would expect the following:
EF Core version
10.0.11
Database provider
Npgsql.EntityFrameworkCore.PostgreSQL (but I don't think it matters)
Target framework
.NET 10
Operating system
Windows 11
IDE
Rider, VS 2026