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
15 changes: 15 additions & 0 deletions src/EFCore/EFCore.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -3787,6 +3787,18 @@
{
"Member": "static string ComplexCollectionWrongClrType(object? property, object? type, object? clrType, object? targetType);"
},
{
"Member": "static string ComplexPropertyChainIntermediateNotFound(object? member, object? type);"
},
{
"Member": "static string ComplexPropertyChainInvalidMember(object? member, object? type);"
},
{
"Member": "static string ComplexPropertyChainInvalidSegment(object? dottedName);"
},
{
"Member": "static string ComplexPropertyChainOnCollection(object? member, object? type);"
},
{
"Member": "static string ComplexPropertyIndexer(object? type, object? property);"
},
Expand Down Expand Up @@ -4154,6 +4166,9 @@
{
"Member": "static string InvalidKeyValue(object? entityType, object? keyProperty);"
},
{
"Member": "static string InvalidMemberAccessChainExpression(object? expression);"
},
{
"Member": "static string InvalidMemberExpression(object? expression);"
},
Expand Down
35 changes: 32 additions & 3 deletions src/EFCore/Extensions/Internal/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,37 @@ var memberInfos
return memberInfos?.Count == 1 ? memberInfos[0] : null;
}

private static IReadOnlyList<TMemberInfo>? MatchMemberAccess<TMemberInfo>(
/// <summary>
/// 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
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static List<MemberInfo>? MatchMemberAccessChain(
this LambdaExpression lambdaExpression)
{
Check.DebugAssert(
lambdaExpression.Parameters.Count == 1,
$"Parameters.Count is {lambdaExpression.Parameters.Count}");

return MatchMemberAccess<MemberInfo>(lambdaExpression.Parameters[0], lambdaExpression.Body);
}

/// <summary>
/// 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
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static List<MemberInfo> GetMemberAccessChain(
this LambdaExpression expression,
string parameterName)
=> expression.MatchMemberAccessChain()
?? throw new ArgumentException(
CoreStrings.InvalidMemberAccessChainExpression(expression),
parameterName);
Comment thread
AndriySvyryd marked this conversation as resolved.

private static List<TMemberInfo>? MatchMemberAccess<TMemberInfo>(
this Expression parameterExpression,
Expression memberAccessExpression)
where TMemberInfo : MemberInfo
Expand All @@ -132,8 +162,7 @@ var memberInfos
do
{
var memberExpression = unwrappedExpression as MemberExpression;

if (!(memberExpression?.Member is TMemberInfo memberInfo))
if (memberExpression?.Member is not TMemberInfo memberInfo)
{
return null;
}
Expand Down
24 changes: 1 addition & 23 deletions src/EFCore/Infrastructure/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,29 +176,7 @@ private static TMemberInfo GetInternalMemberAccess<TMemberInfo>(this LambdaExpre
nameof(memberAccessExpression));
}

var declaringType = memberInfo.DeclaringType;
var parameterType = parameterExpression.Type;

if (declaringType != null
&& declaringType != parameterType
&& declaringType.IsInterface
&& declaringType.IsAssignableFrom(parameterType)
&& memberInfo is PropertyInfo propertyInfo)
{
var propertyGetter = propertyInfo.GetMethod;
var interfaceMapping = parameterType.GetTypeInfo().GetRuntimeInterfaceMap(declaringType);
var index = Array.FindIndex(interfaceMapping.InterfaceMethods, p => p.Equals(propertyGetter));
var targetMethod = interfaceMapping.TargetMethods[index];
foreach (var runtimeProperty in parameterType.GetRuntimeProperties())
{
if (targetMethod.Equals(runtimeProperty.GetMethod))
{
return (TMemberInfo)(object)runtimeProperty;
}
}
}

return memberInfo;
return (TMemberInfo)memberInfo.ResolveMemberForType(parameterExpression.Type);
}

/// <summary>
Expand Down
151 changes: 84 additions & 67 deletions src/EFCore/Metadata/Builders/ComplexCollectionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,12 @@ public virtual ComplexCollectionBuilder IsRequired(bool required = true)
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexCollectionTypePropertyBuilder Property(string propertyName)
=> new(
TypeBuilder.Property(
Check.NotEmpty(propertyName),
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.Property(leafName, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a property of the complex type.
Expand All @@ -139,10 +141,12 @@ public virtual ComplexCollectionTypePropertyBuilder Property(string propertyName
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexCollectionTypePropertyBuilder<TProperty> Property<TProperty>(string propertyName)
=> new(
TypeBuilder.Property(
typeof(TProperty),
Check.NotEmpty(propertyName), ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.Property(typeof(TProperty), leafName, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a property of the complex type.
Expand All @@ -159,10 +163,13 @@ public virtual ComplexCollectionTypePropertyBuilder<TProperty> Property<TPropert
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexCollectionTypePropertyBuilder Property(Type propertyType, string propertyName)
=> new(
TypeBuilder.Property(
Check.NotNull(propertyType),
Check.NotEmpty(propertyName), ConfigurationSource.Explicit)!.Metadata);
{
Check.NotNull(propertyType);
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.Property(propertyType, leafName, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a property of the complex type where that property represents
Expand All @@ -177,10 +184,12 @@ public virtual ComplexCollectionTypePropertyBuilder Property(Type propertyType,
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexTypePrimitiveCollectionBuilder PrimitiveCollection(string propertyName)
=> new(
TypeBuilder.PrimitiveCollection(
Check.NotEmpty(propertyName),
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.PrimitiveCollection(leafName, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a property of the complex type where that property represents
Expand All @@ -198,10 +207,12 @@ public virtual ComplexTypePrimitiveCollectionBuilder PrimitiveCollection(string
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexTypePrimitiveCollectionBuilder<TProperty> PrimitiveCollection<TProperty>(string propertyName)
=> new(
TypeBuilder.PrimitiveCollection(
typeof(TProperty),
Check.NotEmpty(propertyName), ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.PrimitiveCollection(typeof(TProperty), leafName, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a property of the complex type where that property represents
Expand All @@ -219,10 +230,13 @@ public virtual ComplexTypePrimitiveCollectionBuilder<TProperty> PrimitiveCollect
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexTypePrimitiveCollectionBuilder PrimitiveCollection(Type propertyType, string propertyName)
=> new(
TypeBuilder.PrimitiveCollection(
Check.NotNull(propertyType),
Check.NotEmpty(propertyName), ConfigurationSource.Explicit)!.Metadata);
{
Check.NotNull(propertyType);
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.PrimitiveCollection(propertyType, leafName, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a property of the complex type.
Expand Down Expand Up @@ -279,13 +293,13 @@ public virtual ComplexCollectionTypePropertyBuilder IndexerProperty(
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexPropertyBuilder ComplexProperty(string propertyName)
=> new(
TypeBuilder.ComplexProperty(
propertyType: null,
Check.NotEmpty(propertyName),
complexTypeName: null,
collection: false,
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.ComplexProperty(
propertyType: null, leafName, complexTypeName: null, collection: false, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a complex property of the complex type.
Expand All @@ -303,13 +317,13 @@ public virtual ComplexPropertyBuilder ComplexProperty(string propertyName)
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexPropertyBuilder<TProperty> ComplexProperty<TProperty>(string propertyName)
where TProperty : notnull
=> new(
TypeBuilder.ComplexProperty(
typeof(TProperty),
Check.NotEmpty(propertyName),
complexTypeName: null,
collection: false,
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.ComplexProperty(
typeof(TProperty), leafName, complexTypeName: null, collection: false, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a complex property of the complex type.
Expand Down Expand Up @@ -351,13 +365,14 @@ public virtual ComplexPropertyBuilder<TProperty> ComplexProperty<TProperty>(stri
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexPropertyBuilder ComplexProperty(Type propertyType, string propertyName)
=> new(
TypeBuilder.ComplexProperty(
Check.NotNull(propertyType),
Check.NotEmpty(propertyName),
complexTypeName: null,
collection: false,
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotNull(propertyType);
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.ComplexProperty(
propertyType, leafName, complexTypeName: null, collection: false, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a complex property of the complex type.
Expand Down Expand Up @@ -528,13 +543,13 @@ public virtual ComplexCollectionBuilder ComplexProperty(
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexCollectionBuilder ComplexCollection(string propertyName)
=> new(
TypeBuilder.ComplexProperty(
propertyType: null,
Check.NotEmpty(propertyName),
complexTypeName: null,
collection: true,
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.ComplexProperty(
propertyType: null, leafName, complexTypeName: null, collection: true, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a complex collection of the complex type.
Expand All @@ -554,13 +569,13 @@ public virtual ComplexCollectionBuilder ComplexCollection(string propertyName)
public virtual ComplexCollectionBuilder<TElement> ComplexCollection<TProperty, TElement>(string propertyName)
where TProperty : IEnumerable<TElement>
where TElement : notnull
=> new(
TypeBuilder.ComplexProperty(
typeof(TProperty),
Check.NotEmpty(propertyName),
complexTypeName: null,
collection: true,
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.ComplexProperty(
typeof(TProperty), leafName, complexTypeName: null, collection: true, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a complex collection of the complex type.
Expand Down Expand Up @@ -604,13 +619,14 @@ public virtual ComplexCollectionBuilder<TElement> ComplexCollection<TProperty, T
/// <param name="propertyName">The name of the property to be configured.</param>
/// <returns>An object that can be used to configure the property.</returns>
public virtual ComplexCollectionBuilder ComplexCollection(Type propertyType, string propertyName)
=> new(
TypeBuilder.ComplexProperty(
Check.NotNull(propertyType),
Check.NotEmpty(propertyName),
complexTypeName: null,
collection: true,
ConfigurationSource.Explicit)!.Metadata);
{
Check.NotNull(propertyType);
Check.NotEmpty(propertyName);

var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
return new(innerBuilder.ComplexProperty(
propertyType, leafName, complexTypeName: null, collection: true, ConfigurationSource.Explicit)!.Metadata);
}

/// <summary>
/// Returns an object that can be used to configure a complex collection of the complex type.
Expand Down Expand Up @@ -782,7 +798,8 @@ public virtual ComplexCollectionBuilder Ignore(string propertyName)
{
Check.NotEmpty(propertyName);

TypeBuilder.Ignore(propertyName, ConfigurationSource.Explicit);
var (innerBuilder, leafName) = TypeBuilder.ResolveComplexChainByName(propertyName);
innerBuilder.Ignore(leafName, ConfigurationSource.Explicit);

return this;
}
Expand Down
Loading
Loading