Skip to content

Commit c218ae1

Browse files
authored
Fix shadow discriminators on complex types by skipping shadow properties on complex types in snapshot creation (#37394)
Fixes #37337
1 parent a154024 commit c218ae1

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
1414
/// </summary>
1515
public abstract class SnapshotFactoryFactory
1616
{
17+
private static readonly bool UseOldBehavior37337 =
18+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37337", out var enabled) && enabled;
19+
1720
/// <summary>
1821
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1922
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -139,6 +142,14 @@ protected virtual Expression CreateSnapshotExpression(
139142
continue;
140143

141144
case IProperty property:
145+
// Shadow property materialization on complex types is not currently supported (see #35613).
146+
if (!UseOldBehavior37337 && propertyBase.DeclaringType is IComplexType && property.IsShadowProperty())
147+
{
148+
arguments[i] = propertyBase.ClrType.GetDefaultValueConstant();
149+
types[i] = propertyBase.ClrType;
150+
continue;
151+
}
152+
142153
arguments[i] = CreateSnapshotValueExpression(CreateReadValueExpression(parameter, property), property);
143154
continue;
144155

test/EFCore.Specification.Tests/Query/AdHocComplexTypeQueryTestBase.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,65 @@ public class ComplexTypeWithAllNulls
268268

269269
#endregion 37162
270270

271+
#region Issue37337
272+
273+
[ConditionalFact]
274+
public virtual async Task Nullable_complex_type_with_discriminator_and_shadow_property()
275+
{
276+
var contextFactory = await InitializeAsync<Context37337>(
277+
seed: context =>
278+
{
279+
context.Add(
280+
new Context37337.EntityType
281+
{
282+
Prop = new Context37337.OptionalComplexProperty
283+
{
284+
OptionalValue = true
285+
}
286+
});
287+
return context.SaveChangesAsync();
288+
});
289+
290+
await using var context = contextFactory.CreateContext();
291+
292+
var entities = await context.Set<Context37337.EntityType>().ToArrayAsync();
293+
294+
Assert.Single(entities);
295+
var entity = entities[0];
296+
Assert.NotNull(entity.Prop);
297+
Assert.True(entity.Prop.OptionalValue);
298+
}
299+
300+
private class Context37337(DbContextOptions options) : DbContext(options)
301+
{
302+
protected override void OnModelCreating(ModelBuilder modelBuilder)
303+
{
304+
var entity = modelBuilder.Entity<EntityType>();
305+
entity.Property(p => p.Id);
306+
entity.HasKey(p => p.Id);
307+
308+
var compl = entity.ComplexProperty(p => p.Prop);
309+
compl.Property(p => p.OptionalValue);
310+
compl.HasDiscriminator();
311+
312+
// Shadow property added via convention (e.g., audit field)
313+
entity.Property<string>("CreatedBy").IsRequired(false);
314+
}
315+
316+
public class EntityType
317+
{
318+
public Guid Id { get; set; }
319+
public OptionalComplexProperty? Prop { get; set; }
320+
}
321+
322+
public class OptionalComplexProperty
323+
{
324+
public bool? OptionalValue { get; set; }
325+
}
326+
}
327+
328+
#endregion Issue37337
329+
271330
protected override string StoreName
272331
=> "AdHocComplexTypeQueryTest";
273332
}

0 commit comments

Comments
 (0)