mirror of
https://github.com/zoriya/EntityFrameworkCore.Projectables.git
synced 2026-08-15 18:44:57 +00:00
Find overriding implementations for virtual methods
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -21,5 +24,90 @@ namespace EntityFrameworkCore.Projectables.Extensions
|
||||
|
||||
yield return type;
|
||||
}
|
||||
|
||||
public static MethodInfo GetOverridingMethod(this Type derivedType, MethodInfo methodInfo)
|
||||
{
|
||||
// We only need to search for virtual instance methods who are not declared on the derivedType
|
||||
if (derivedType == methodInfo.DeclaringType || methodInfo.IsStatic || !methodInfo.IsVirtual)
|
||||
{
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
if (!derivedType.IsAssignableTo(methodInfo.DeclaringType))
|
||||
{
|
||||
throw new ArgumentException("MethodInfo needs to be declared on the type hierarchy", nameof(methodInfo));
|
||||
}
|
||||
|
||||
var derivedMethods = derivedType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
foreach (var derivedMethodInfo in derivedMethods)
|
||||
{
|
||||
if (HasCompatibleSignature(methodInfo, derivedMethodInfo))
|
||||
{
|
||||
return derivedMethodInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// No derived methods were found. Return the original methodInfo
|
||||
return methodInfo;
|
||||
|
||||
static bool HasCompatibleSignature(MethodInfo methodInfo, MethodInfo derivedMethodInfo)
|
||||
{
|
||||
if (methodInfo.Name != derivedMethodInfo.Name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var methodParameters = methodInfo.GetParameters();
|
||||
|
||||
var derivedMethodParameters = derivedMethodInfo.GetParameters();
|
||||
if (methodParameters.Length != derivedMethodParameters.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match all parameters
|
||||
for (var parameterIndex = 0; parameterIndex < methodParameters.Length; parameterIndex++)
|
||||
{
|
||||
var parameter = methodParameters[parameterIndex];
|
||||
var derivedParameter = derivedMethodParameters[parameterIndex];
|
||||
|
||||
if (parameter.ParameterType.IsGenericParameter)
|
||||
{
|
||||
if (!derivedParameter.ParameterType.IsGenericParameter)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parameter.ParameterType != derivedParameter.ParameterType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Match the number of generic type arguments
|
||||
if (methodInfo.IsGenericMethodDefinition)
|
||||
{
|
||||
var methodGenericParameters = methodInfo.GetGenericArguments();
|
||||
|
||||
if (!derivedMethodInfo.IsGenericMethodDefinition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var derivedGenericArguments = derivedMethodInfo.GetGenericArguments();
|
||||
|
||||
if (methodGenericParameters.Length != derivedGenericArguments.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
@@ -7,6 +8,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using EntityFrameworkCore.Projectables.Extensions;
|
||||
|
||||
namespace EntityFrameworkCore.Projectables.Services
|
||||
{
|
||||
@@ -39,7 +41,10 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
|
||||
protected override Expression VisitMethodCall(MethodCallExpression node)
|
||||
{
|
||||
if (TryGetReflectedExpression(node.Method, out var reflectedExpression))
|
||||
// Get the overriding methodInfo based on te type of the received of this expression
|
||||
var methodInfo = node.Object?.Type.GetOverridingMethod(node.Method) ?? node.Method;
|
||||
|
||||
if (TryGetReflectedExpression(methodInfo, out var reflectedExpression))
|
||||
{
|
||||
for (var parameterIndex = 0; parameterIndex < reflectedExpression.Parameters.Count; parameterIndex++)
|
||||
{
|
||||
@@ -69,7 +74,12 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
|
||||
protected override Expression VisitMember(MemberExpression node)
|
||||
{
|
||||
if (TryGetReflectedExpression(node.Member, out var reflectedExpression))
|
||||
var nodeMember = node.Expression switch {
|
||||
{ Type: { } } => node.Expression.Type.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)[0],
|
||||
_ => node.Member
|
||||
};
|
||||
|
||||
if (TryGetReflectedExpression(nodeMember, out var reflectedExpression))
|
||||
{
|
||||
if (node.Expression is not null)
|
||||
{
|
||||
|
||||
@@ -14,15 +14,15 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
{
|
||||
public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo)
|
||||
{
|
||||
var reflectedType = projectableMemberInfo.ReflectedType ?? throw new InvalidOperationException("Expected a valid type here");
|
||||
var generatedContainingTypeName = ProjectionExpressionClassNameGenerator.GenerateFullName(reflectedType.Namespace, reflectedType.GetNestedTypePath().Select(x => x.Name), projectableMemberInfo.Name);
|
||||
var declaringType = projectableMemberInfo.DeclaringType ?? throw new InvalidOperationException("Expected a valid type here");
|
||||
var generatedContainingTypeName = ProjectionExpressionClassNameGenerator.GenerateFullName(declaringType.Namespace, declaringType.GetNestedTypePath().Select(x => x.Name), projectableMemberInfo.Name);
|
||||
|
||||
var genericArguments = projectableMemberInfo switch {
|
||||
MethodInfo methodInfo => methodInfo.GetGenericArguments(),
|
||||
_ => null
|
||||
};
|
||||
|
||||
var expressionFactoryMethod = reflectedType.Assembly.GetType(generatedContainingTypeName)
|
||||
var expressionFactoryMethod = declaringType.Assembly.GetType(generatedContainingTypeName)
|
||||
?.GetMethods()
|
||||
?.FirstOrDefault();
|
||||
|
||||
@@ -40,20 +40,20 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
|
||||
if (useMemberBody is not null)
|
||||
{
|
||||
var exprProperty = reflectedType.GetProperty(useMemberBody, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
var exprProperty = declaringType.GetProperty(useMemberBody, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
var lambda = exprProperty?.GetValue(null) as LambdaExpression;
|
||||
|
||||
if (lambda is not null)
|
||||
{
|
||||
if (projectableMemberInfo is PropertyInfo property &&
|
||||
lambda.Parameters.Count == 1 &&
|
||||
lambda.Parameters[0].Type == reflectedType && lambda.ReturnType == property.PropertyType)
|
||||
lambda.Parameters[0].Type == declaringType && lambda.ReturnType == property.PropertyType)
|
||||
{
|
||||
return lambda;
|
||||
}
|
||||
else if (projectableMemberInfo is MethodInfo method &&
|
||||
lambda.Parameters.Count == method.GetParameters().Length + 1 &&
|
||||
lambda.Parameters.Last().Type == reflectedType &&
|
||||
lambda.Parameters.Last().Type == declaringType &&
|
||||
!lambda.Parameters.Zip(method.GetParameters(), (a, b) => a.Type != b.ParameterType).Any())
|
||||
{
|
||||
return lambda;
|
||||
@@ -62,8 +62,8 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
}
|
||||
|
||||
var fullName = string.Join(".", Enumerable.Empty<string>()
|
||||
.Concat(new[] { reflectedType.Namespace })
|
||||
.Concat(reflectedType.GetNestedTypePath().Select(x => x.Name))
|
||||
.Concat(new[] { declaringType.Namespace })
|
||||
.Concat(declaringType.GetNestedTypePath().Select(x => x.Name))
|
||||
.Concat(new[] { projectableMemberInfo.Name }));
|
||||
|
||||
throw new InvalidOperationException($"Unable to resolve generated expression for {fullName}.") {
|
||||
|
||||
Reference in New Issue
Block a user