Skip to content

Commit

Permalink
Handle complex types in GetDatabaseValues (#32813)
Browse files Browse the repository at this point in the history
Fixes #32701
Fixes #31353
  • Loading branch information
ajcvickers committed Jan 23, 2024
1 parent 1b51aa3 commit 1c728fa
Show file tree
Hide file tree
Showing 7 changed files with 441 additions and 141 deletions.
4 changes: 2 additions & 2 deletions src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public override void SetValues(PropertyValues propertyValues)

for (var i = 0; i < _values.Length; i++)
{
SetValue(i, propertyValues[Properties[i].Name]);
SetValue(i, propertyValues[Properties[i]]);
}
}

Expand All @@ -110,7 +110,7 @@ public override void SetValues(PropertyValues propertyValues)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override IReadOnlyList<IProperty> Properties
=> _properties ??= EntityType.GetProperties().ToList();
=> _properties ??= EntityType.GetFlattenedProperties().ToList();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public override void SetValues(PropertyValues propertyValues)

foreach (var property in Properties)
{
SetValueInternal(property, propertyValues[property.Name]);
SetValueInternal(property, propertyValues[property]);
}
}

Expand All @@ -107,7 +107,7 @@ public override void SetValues(PropertyValues propertyValues)
public override IReadOnlyList<IProperty> Properties
{
[DebuggerStepThrough]
get => _properties ??= EntityType.GetProperties().ToList();
get => _properties ??= EntityType.GetFlattenedProperties().ToList();
}

/// <summary>
Expand Down
27 changes: 22 additions & 5 deletions src/EFCore/Internal/EntityFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -859,15 +859,32 @@ private static Expression<Func<object, object[]>> BuildProjection(IEntityType en
var entityParameter = Expression.Parameter(typeof(object), "e");

var projections = new List<Expression>();
foreach (var property in entityType.GetProperties())
foreach (var property in entityType.GetFlattenedProperties())
{
var path = new List<IPropertyBase> { property };
while (path[^1].DeclaringType is IComplexType complexType)
{
path.Add(complexType.ComplexProperty);
}

Expression instanceExpression = entityParameter;
for (var i = path.Count - 1; i >= 0; i--)
{
instanceExpression = Expression.Call(
EF.PropertyMethod.MakeGenericMethod(path[i].ClrType),
instanceExpression,
Expression.Constant(path[i].Name, typeof(string)));

if (i != 0 && instanceExpression.Type.IsValueType)
{
instanceExpression = Expression.Convert(instanceExpression, typeof(object));
}
}

projections.Add(
Expression.Convert(
Expression.Convert(
Expression.Call(
EF.PropertyMethod.MakeGenericMethod(property.ClrType),
entityParameter,
Expression.Constant(property.Name, typeof(string))),
instanceExpression,
property.ClrType),
typeof(object)));
}
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Metadata/Internal/EntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ public static IProperty CheckPropertyBelongsToType(
{
Check.NotNull(property, nameof(property));

if ((property.DeclaringType as IEntityType)?.IsAssignableFrom(entityType) != true)
if (!property.DeclaringType.ContainingEntityType.IsAssignableFrom(entityType))
{
throw new InvalidOperationException(
CoreStrings.PropertyDoesNotBelong(property.Name, property.DeclaringType.DisplayName(), entityType.DisplayName()));
Expand Down
7 changes: 4 additions & 3 deletions src/EFCore/Query/Internal/EntityMaterializerSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,16 @@ public Expression CreateMaterializeExpression(

var constructorExpression = constructorBinding.CreateConstructorExpression(bindingInfo);

if (_materializationInterceptor == null)
if (_materializationInterceptor == null
// TODO: This currently applies the materialization interceptor only on the root structural type - any contained complex types
// don't get intercepted.
|| structuralType is not IEntityType)
{
return properties.Count == 0 && blockExpressions.Count == 0
? constructorExpression
: CreateMaterializeExpression(blockExpressions, instanceVariable, constructorExpression, properties, bindingInfo);
}

// TODO: This currently applies the materialization interceptor only on the root structural type - any contained complex types
// don't get intercepted.
return CreateInterceptionMaterializeExpression(
structuralType,
properties,
Expand Down
37 changes: 32 additions & 5 deletions test/EFCore.InMemory.FunctionalTests/PropertyValuesInMemoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,44 @@ namespace Microsoft.EntityFrameworkCore;

public class PropertyValuesInMemoryTest : PropertyValuesTestBase<PropertyValuesInMemoryTest.PropertyValuesInMemoryFixture>
{
public PropertyValuesInMemoryTest(PropertyValuesInMemoryFixture fixture)
: base(fixture)
{
}
public override Task Complex_current_values_can_be_accessed_as_a_property_dictionary_using_IProperty()
=> Assert.ThrowsAsync<NullReferenceException>( // In-memory database cannot query complex types
() => base.Complex_current_values_can_be_accessed_as_a_property_dictionary_using_IProperty());

public override Task Complex_original_values_can_be_accessed_as_a_property_dictionary_using_IProperty()
=> Assert.ThrowsAsync<NullReferenceException>( // In-memory database cannot query complex types
() => base.Complex_original_values_can_be_accessed_as_a_property_dictionary_using_IProperty());

public override Task Complex_store_values_can_be_accessed_as_a_property_dictionary_using_IProperty()
=> Assert.ThrowsAsync<NullReferenceException>( // In-memory database cannot query complex types
() => base.Complex_store_values_can_be_accessed_as_a_property_dictionary_using_IProperty());

public override Task Complex_store_values_can_be_accessed_asynchronously_as_a_property_dictionary_using_IProperty()
=> Assert.ThrowsAsync<NullReferenceException>( // In-memory database cannot query complex types
() => base.Complex_store_values_can_be_accessed_asynchronously_as_a_property_dictionary_using_IProperty());

public class PropertyValuesInMemoryFixture : PropertyValuesFixtureBase
{
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
=> base.AddOptions(builder).EnableSensitiveDataLogging(false);
=> base.AddOptions(builder)
.ConfigureWarnings(w => w.Ignore(CoreEventId.MappedComplexPropertyIgnoredWarning))
.EnableSensitiveDataLogging(false);

protected override ITestStoreFactory TestStoreFactory
=> InMemoryTestStoreFactory.Instance;

protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
{
base.OnModelCreating(modelBuilder, context);

// In-memory database doesn't support complex type queries
modelBuilder.Entity<Building>(
b =>
{
b.Ignore(e => e.Culture);
b.Ignore(e => e.Milk);
});

}
}
}
Loading

0 comments on commit 1c728fa

Please sign in to comment.