diff --git a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs index b1dbf173f47..3b8be38be64 100644 --- a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs @@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; /// public abstract class SnapshotFactoryFactory { + private static readonly bool UseOldBehavior37337 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37337", out var enabled) && enabled; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -139,6 +142,14 @@ protected virtual Expression CreateSnapshotExpression( continue; case IProperty property: + // Shadow property materialization on complex types is not currently supported (see #35613). + if (!UseOldBehavior37337 && propertyBase.DeclaringType is IComplexType && property.IsShadowProperty()) + { + arguments[i] = propertyBase.ClrType.GetDefaultValueConstant(); + types[i] = propertyBase.ClrType; + continue; + } + arguments[i] = CreateSnapshotValueExpression(CreateReadValueExpression(parameter, property), property); continue; diff --git a/test/EFCore.Specification.Tests/Query/AdHocComplexTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/AdHocComplexTypeQueryTestBase.cs index bd927bd2906..c660e8aa42f 100644 --- a/test/EFCore.Specification.Tests/Query/AdHocComplexTypeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/AdHocComplexTypeQueryTestBase.cs @@ -268,6 +268,65 @@ public class ComplexTypeWithAllNulls #endregion 37162 + #region Issue37337 + + [ConditionalFact] + public virtual async Task Nullable_complex_type_with_discriminator_and_shadow_property() + { + var contextFactory = await InitializeAsync( + seed: context => + { + context.Add( + new Context37337.EntityType + { + Prop = new Context37337.OptionalComplexProperty + { + OptionalValue = true + } + }); + return context.SaveChangesAsync(); + }); + + await using var context = contextFactory.CreateContext(); + + var entities = await context.Set().ToArrayAsync(); + + Assert.Single(entities); + var entity = entities[0]; + Assert.NotNull(entity.Prop); + Assert.True(entity.Prop.OptionalValue); + } + + private class Context37337(DbContextOptions options) : DbContext(options) + { + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + var entity = modelBuilder.Entity(); + entity.Property(p => p.Id); + entity.HasKey(p => p.Id); + + var compl = entity.ComplexProperty(p => p.Prop); + compl.Property(p => p.OptionalValue); + compl.HasDiscriminator(); + + // Shadow property added via convention (e.g., audit field) + entity.Property("CreatedBy").IsRequired(false); + } + + public class EntityType + { + public Guid Id { get; set; } + public OptionalComplexProperty? Prop { get; set; } + } + + public class OptionalComplexProperty + { + public bool? OptionalValue { get; set; } + } + } + + #endregion Issue37337 + protected override string StoreName => "AdHocComplexTypeQueryTest"; }