Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;

Expand Down Expand Up @@ -99,7 +100,7 @@ public override void SetValues(PropertyValues propertyValues)

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

Expand All @@ -110,7 +111,9 @@ 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 ??= EntityMaterializerSource.UseOldBehavior32701
? EntityType.GetProperties().ToList()
: EntityType.GetFlattenedProperties().ToList();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
8 changes: 6 additions & 2 deletions src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;

Expand Down Expand Up @@ -94,7 +95,8 @@ public override void SetValues(PropertyValues propertyValues)

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

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

/// <summary>
Expand Down
55 changes: 46 additions & 9 deletions src/EFCore/Internal/EntityFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Internal;

Expand Down Expand Up @@ -859,17 +860,53 @@ 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())

if (EntityMaterializerSource.UseOldBehavior32701)
{
projections.Add(
Expression.Convert(
foreach (var property in entityType.GetProperties())
{
projections.Add(
Expression.Convert(
Expression.Call(
EF.PropertyMethod.MakeGenericMethod(property.ClrType),
entityParameter,
Expression.Constant(property.Name, typeof(string))),
property.ClrType),
typeof(object)));
Expression.Convert(
Expression.Call(
EF.PropertyMethod.MakeGenericMethod(property.ClrType),
entityParameter,
Expression.Constant(property.Name, typeof(string))),
property.ClrType),
typeof(object)));
}
}
else
{
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(
instanceExpression,
property.ClrType),
typeof(object)));
}
}

return Expression.Lambda<Func<object, object[]>>(
Expand Down
6 changes: 5 additions & 1 deletion src/EFCore/Metadata/Internal/EntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// ReSharper disable ArgumentsStyleOther
// ReSharper disable ArgumentsStyleNamedExpression

using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Internal;

/// <summary>
Expand Down Expand Up @@ -309,7 +311,9 @@ public static IProperty CheckPropertyBelongsToType(
{
Check.NotNull(property, nameof(property));

if ((property.DeclaringType as IEntityType)?.IsAssignableFrom(entityType) != true)
if ((EntityMaterializerSource.UseOldBehavior32701
&& (property.DeclaringType as IEntityType)?.IsAssignableFrom(entityType) != true)
|| !property.DeclaringType.ContainingEntityType.IsAssignableFrom(entityType))
{
throw new InvalidOperationException(
CoreStrings.PropertyDoesNotBelong(property.Name, property.DeclaringType.DisplayName(), entityType.DisplayName()));
Expand Down
10 changes: 7 additions & 3 deletions src/EFCore/Query/Internal/EntityMaterializerSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public class EntityMaterializerSource : IEntityMaterializerSource
public static readonly bool UseOldBehavior31866 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31866", out var enabled31866) && enabled31866;

internal static readonly bool UseOldBehavior32701 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32701", out var enabled32701) && enabled32701;

private static readonly MethodInfo InjectableServiceInjectedMethod
= typeof(IInjectableService).GetMethod(nameof(IInjectableService.Injected))!;

Expand Down Expand Up @@ -117,15 +120,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 && !UseOldBehavior32701))
{
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
40 changes: 34 additions & 6 deletions test/EFCore.InMemory.FunctionalTests/PropertyValuesInMemoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,47 @@

namespace Microsoft.EntityFrameworkCore;

public class PropertyValuesInMemoryTest : PropertyValuesTestBase<PropertyValuesInMemoryTest.PropertyValuesInMemoryFixture>
public class PropertyValuesInMemoryTest(PropertyValuesInMemoryTest.PropertyValuesInMemoryFixture fixture)
: PropertyValuesTestBase<PropertyValuesInMemoryTest.PropertyValuesInMemoryFixture>(fixture)
{
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