Skip to content

Commit

Permalink
Use Shadow property index when generating Shadow values buffer
Browse files Browse the repository at this point in the history
Shadow Values factory expects a value buffer which also has slots for shadow navigations. But when populating we did not consider them. So we got a value buffer with values in wrong slots.
It happens only for ModelSnapshot (the shadow model phase) when seeding derived type when base type has a navigation.

It throws NRE if encounter null value for non-null field
or ICE if encounter different type then expected.

Resolves #17851
  • Loading branch information
smitpatel committed Oct 15, 2019
1 parent 59fbd88 commit 1bb4315
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/EFCore/ChangeTracking/Internal/StateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ public virtual InternalEntityEntry GetOrCreateEntry(object entity, IEntityType e
/// </summary>
public virtual InternalEntityEntry CreateEntry(IDictionary<string, object> values, IEntityType entityType)
{
var (i, j) = (0, 0);
var i = 0;
var valuesArray = new object[entityType.PropertyCount()];
var shadowPropertyValuesArray = new object[entityType.ShadowPropertyCount()];
foreach (var property in entityType.GetProperties())
Expand All @@ -280,7 +280,7 @@ public virtual InternalEntityEntry CreateEntry(IDictionary<string, object> value

if (property.IsShadowProperty())
{
shadowPropertyValuesArray[j++] = values.TryGetValue(property.Name, out var shadowValue)
shadowPropertyValuesArray[property.GetShadowIndex()] = values.TryGetValue(property.Name, out var shadowValue)
? shadowValue
: property.ClrType.GetDefaultValue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7948,6 +7948,150 @@ public void Create_table_handles_same_name_but_different_schemas_and_identifying
});
}

[ConditionalFact]
public void Construction_of_shadow_values_buffer_account_for_shadow_navigations_1()
{
Execute(
modelBuilder => modelBuilder
.Entity(
"User",
b =>
{
b.Property<int>("Id");
b.ToTable("Users");
b.HasData(new { Id = 1 });
})
.Entity(
"BaseType",
b =>
{
b.Property<int>("Id");
b.Property<string>("Discriminator");
b.Property<int>("UserId");
b.ToTable("Type");
b.HasDiscriminator<string>("Discriminator").HasValue("BaseType");
})
.Entity(
"DerivedType",
b =>
{
b.HasBaseType("BaseType");
b.Property<int>("LevelId");
b.HasDiscriminator().HasValue("DerivedType");
b.HasData(new { Id = 1, UserId = 1, LevelId = 1 });
})
.Entity("BaseType")
.HasOne("User", "User")
.WithMany()
.HasForeignKey("UserId"),
modelBuilder => modelBuilder
.Entity(
"User",
b =>
{
b.Property<int>("Id");
b.ToTable("Users");
b.HasData(new { Id = 1 });
})
.Entity(
"BaseType",
b =>
{
b.Property<int>("Id");
b.Property<string>("Discriminator");
b.Property<int>("UserId");
b.ToTable("Type");
b.HasDiscriminator<string>("Discriminator").HasValue("BaseType");
})
.Entity(
"DerivedType",
b =>
{
b.HasBaseType("BaseType");
b.Property<int>("LevelId");
b.HasDiscriminator().HasValue("DerivedType");
b.HasData(new { Id = 1, UserId = 1, LevelId = 1 });
})
.Entity("BaseType")
.HasOne("User", "User")
.WithMany()
.HasForeignKey("UserId"),
ops => { });
}

[ConditionalFact]
public void Construction_of_shadow_values_buffer_account_for_shadow_navigations_2()
{
Execute(
modelBuilder => modelBuilder
.Entity(
"User",
b =>
{
b.Property<int>("Id");
b.ToTable("Users");
b.HasData(new { Id = 1 });
})
.Entity(
"BaseType",
b =>
{
b.Property<int>("Id");
b.Property<string>("Discriminator");
b.Property<int>("UserId");
b.ToTable("Type");
b.HasDiscriminator<string>("Discriminator").HasValue("BaseType");
})
.Entity(
"DerivedType",
b =>
{
b.HasBaseType("BaseType");
b.Property<int>("Level1Id");
b.Property<decimal>("Level2Id");
b.HasDiscriminator().HasValue("DerivedType");
b.HasData(new { Id = 1, UserId = 1, Level1Id = 1, Level2Id = 1.0 });
})
.Entity("BaseType")
.HasOne("User", "User")
.WithMany()
.HasForeignKey("UserId"),
modelBuilder => modelBuilder
.Entity(
"User",
b =>
{
b.Property<int>("Id");
b.ToTable("Users");
b.HasData(new { Id = 1 });
})
.Entity(
"BaseType",
b =>
{
b.Property<int>("Id");
b.Property<string>("Discriminator");
b.Property<int>("UserId");
b.ToTable("Type");
b.HasDiscriminator<string>("Discriminator").HasValue("BaseType");
})
.Entity(
"DerivedType",
b =>
{
b.HasBaseType("BaseType");
b.Property<int>("Level1Id");
b.Property<double>("Level2Id");
b.HasDiscriminator().HasValue("DerivedType");
b.HasData(new { Id = 1, UserId = 1, Level1Id = 1, Level2Id = 1.0 });
})
.Entity("BaseType")
.HasOne("User", "User")
.WithMany()
.HasForeignKey("UserId"),
ops => { });
}

protected override TestHelpers TestHelpers => RelationalTestHelpers.Instance;
}
}

0 comments on commit 1bb4315

Please sign in to comment.