mirror of
https://github.com/zoriya/EntityFrameworkCore.Projectables.git
synced 2026-08-15 18:44:57 +00:00
Support for declaring projectables on generic classes
This commit is contained in:
@@ -28,7 +28,7 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
visitedNode = visitedParameterSyntax.WithModifiers(node.Modifiers.RemoveAt(thisKeywordIndex));
|
||||
}
|
||||
|
||||
// Strip the parans keyword of any parameter
|
||||
// Strip the params keyword of any parameter
|
||||
var paramsKeywordIndex = ((ParameterSyntax)visitedNode).Modifiers.IndexOf(SyntaxKind.ParamsKeyword);
|
||||
if (paramsKeywordIndex != -1)
|
||||
{
|
||||
@@ -73,6 +73,19 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
return base.VisitIdentifierName(node);
|
||||
}
|
||||
|
||||
public override SyntaxNode? VisitGenericName(GenericNameSyntax node)
|
||||
{
|
||||
var typeInfo = _semanticModel.GetTypeInfo(node);
|
||||
if (typeInfo.Type is not null)
|
||||
{
|
||||
return SyntaxFactory.ParseTypeName(
|
||||
typeInfo.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
).WithTriviaFrom(node);
|
||||
}
|
||||
|
||||
return base.VisitGenericName(node);
|
||||
}
|
||||
|
||||
public override SyntaxNode? VisitQualifiedName(QualifiedNameSyntax node)
|
||||
{
|
||||
var typeInfo = _semanticModel.GetTypeInfo(node);
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
<NoWarn>$(NoWarn);NU5128</NoWarn>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -11,8 +11,6 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
{
|
||||
public class ProjectableDescriptor
|
||||
{
|
||||
public IEnumerable<string>? UsingDirectives { get; set; }
|
||||
|
||||
public string? ClassNamespace { get; set; }
|
||||
|
||||
public IEnumerable<string>? NestedInClassNames { get; set; }
|
||||
@@ -23,6 +21,10 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
|
||||
public string? ClassName { get; set; }
|
||||
|
||||
public TypeParameterListSyntax? ClassTypeParameterList { get; set; }
|
||||
|
||||
public SyntaxList<TypeParameterConstraintClauseSyntax>? ClassConstraintClauses { get; set; }
|
||||
|
||||
public string? MemberName { get; set; }
|
||||
|
||||
public string? ReturnTypeName { get; set; }
|
||||
@@ -31,8 +33,8 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
|
||||
public TypeParameterListSyntax? TypeParameterList { get; set; }
|
||||
|
||||
public IEnumerable<TypeParameterConstraintClauseSyntax>? ConstraintClauses { get; set; }
|
||||
public SyntaxList<TypeParameterConstraintClauseSyntax>? ConstraintClauses { get; set; }
|
||||
|
||||
public SyntaxNode? Body { get; set; }
|
||||
public ExpressionSyntax? ExpressionBody { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,23 +123,52 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
ClassNamespace = memberSymbol.ContainingType.ContainingNamespace.IsGlobalNamespace ? null : memberSymbol.ContainingType.ContainingNamespace.ToDisplayString(),
|
||||
MemberName = memberSymbol.Name,
|
||||
NestedInClassNames = GetNestedInClassPath(memberSymbol.ContainingType),
|
||||
ParametersList = SyntaxFactory.ParameterList(),
|
||||
TypeParameterList = SyntaxFactory.TypeParameterList()
|
||||
ParametersList = SyntaxFactory.ParameterList()
|
||||
};
|
||||
|
||||
if (memberSymbol.ContainingType is INamedTypeSymbol { IsGenericType: true } containingNamedType)
|
||||
{
|
||||
descriptor.ClassTypeParameterList = SyntaxFactory.TypeParameterList();
|
||||
|
||||
foreach (var additionalClassTypeParameter in containingNamedType.TypeParameters)
|
||||
{
|
||||
descriptor.ClassTypeParameterList = descriptor.ClassTypeParameterList.AddParameters(
|
||||
SyntaxFactory.TypeParameter(additionalClassTypeParameter.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
|
||||
);
|
||||
|
||||
if (!additionalClassTypeParameter.ConstraintTypes.IsDefaultOrEmpty)
|
||||
{
|
||||
descriptor.ClassConstraintClauses ??= SyntaxFactory.List<TypeParameterConstraintClauseSyntax>();
|
||||
|
||||
descriptor.ClassConstraintClauses = descriptor.ClassConstraintClauses.Value.Add(
|
||||
SyntaxFactory.TypeParameterConstraintClause(
|
||||
SyntaxFactory.IdentifierName(additionalClassTypeParameter.Name),
|
||||
SyntaxFactory.SeparatedList<TypeParameterConstraintSyntax>(
|
||||
additionalClassTypeParameter
|
||||
.ConstraintTypes
|
||||
.Select(c => SyntaxFactory.TypeConstraint(
|
||||
SyntaxFactory.IdentifierName(c.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
|
||||
))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// todo: add additional type constraints
|
||||
}
|
||||
}
|
||||
|
||||
if (!member.Modifiers.Any(SyntaxKind.StaticKeyword))
|
||||
{
|
||||
descriptor.ParametersList = descriptor.ParametersList.AddParameters(
|
||||
SyntaxFactory.Parameter(
|
||||
SyntaxFactory.Identifier("@this")
|
||||
).WithType(
|
||||
SyntaxFactory.ParseTypeName(
|
||||
memberSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
)
|
||||
.WithTrailingTrivia(
|
||||
SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")
|
||||
)
|
||||
SyntaxFactory.Identifier("@this")
|
||||
)
|
||||
.WithType(
|
||||
SyntaxFactory.ParseTypeName(
|
||||
memberSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -169,7 +198,7 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
var returnType = declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ReturnType);
|
||||
|
||||
descriptor.ReturnTypeName = returnType.ToString();
|
||||
descriptor.Body = expressionSyntaxRewriter.Visit(methodDeclarationSyntax.ExpressionBody.Expression);
|
||||
descriptor.ExpressionBody = (ExpressionSyntax)expressionSyntaxRewriter.Visit(methodDeclarationSyntax.ExpressionBody.Expression);
|
||||
foreach (var additionalParameter in ((ParameterListSyntax)declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ParameterList)).Parameters)
|
||||
{
|
||||
descriptor.ParametersList = descriptor.ParametersList.AddParameters(additionalParameter);
|
||||
@@ -177,6 +206,7 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
|
||||
if (methodDeclarationSyntax.TypeParameterList is not null)
|
||||
{
|
||||
descriptor.TypeParameterList = SyntaxFactory.TypeParameterList();
|
||||
foreach (var additionalTypeParameter in ((TypeParameterListSyntax)declarationSyntaxRewriter.Visit(methodDeclarationSyntax.TypeParameterList)).Parameters)
|
||||
{
|
||||
descriptor.TypeParameterList = descriptor.TypeParameterList.AddParameters(additionalTypeParameter);
|
||||
@@ -185,8 +215,11 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
|
||||
if (methodDeclarationSyntax.ConstraintClauses.Any())
|
||||
{
|
||||
descriptor.ConstraintClauses = methodDeclarationSyntax.ConstraintClauses
|
||||
.Select(x => (TypeParameterConstraintClauseSyntax)declarationSyntaxRewriter.Visit(x));
|
||||
descriptor.ConstraintClauses = SyntaxFactory.List(
|
||||
methodDeclarationSyntax
|
||||
.ConstraintClauses
|
||||
.Select(x => (TypeParameterConstraintClauseSyntax)declarationSyntaxRewriter.Visit(x))
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (memberBody is PropertyDeclarationSyntax propertyDeclarationSyntax)
|
||||
@@ -201,20 +234,13 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
var returnType = declarationSyntaxRewriter.Visit(propertyDeclarationSyntax.Type);
|
||||
|
||||
descriptor.ReturnTypeName = returnType.ToString();
|
||||
descriptor.Body = expressionSyntaxRewriter.Visit(propertyDeclarationSyntax.ExpressionBody.Expression);
|
||||
descriptor.ExpressionBody = (ExpressionSyntax)expressionSyntaxRewriter.Visit(propertyDeclarationSyntax.ExpressionBody.Expression);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
descriptor.UsingDirectives =
|
||||
member.SyntaxTree
|
||||
.GetRoot()
|
||||
.DescendantNodes()
|
||||
.OfType<UsingDirectiveSyntax>()
|
||||
.Select(x => x.ToString());
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace EntityFrameworkCore.Projectables.Generator
|
||||
{
|
||||
@@ -19,6 +21,22 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
{
|
||||
private const string ProjectablesAttributeName = "EntityFrameworkCore.Projectables.ProjectableAttribute";
|
||||
|
||||
static readonly AttributeSyntax _editorBrowsableAttribute =
|
||||
Attribute(
|
||||
ParseName("global::System.ComponentModel.EditorBrowsable"),
|
||||
AttributeArgumentList(
|
||||
SingletonSeparatedList(
|
||||
AttributeArgument(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("global::System.ComponentModel.EditorBrowsableState"),
|
||||
IdentifierName("Never")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// Do a simple filter for members
|
||||
@@ -91,76 +109,84 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
throw new InvalidOperationException("Expected a memberName here");
|
||||
}
|
||||
|
||||
resultBuilder.Clear();
|
||||
|
||||
resultBuilder.AppendLine("// <auto-generated/>");
|
||||
|
||||
if (projectable.UsingDirectives is not null)
|
||||
{
|
||||
foreach (var usingDirective in projectable.UsingDirectives.Distinct())
|
||||
{
|
||||
resultBuilder.AppendLine(usingDirective);
|
||||
}
|
||||
}
|
||||
|
||||
if (projectable.TargetClassNamespace is not null)
|
||||
{
|
||||
var targetClassUsingDirective = $"using {projectable.TargetClassNamespace};";
|
||||
|
||||
if (!projectable.UsingDirectives.Contains(targetClassUsingDirective))
|
||||
{
|
||||
resultBuilder.AppendLine(targetClassUsingDirective);
|
||||
}
|
||||
}
|
||||
|
||||
if (projectable.ClassNamespace is not null && projectable.ClassNamespace != projectable.TargetClassNamespace)
|
||||
{
|
||||
var classUsingDirective = $"using {projectable.ClassNamespace};";
|
||||
|
||||
if (!projectable.UsingDirectives.Contains(classUsingDirective))
|
||||
{
|
||||
resultBuilder.AppendLine(classUsingDirective);
|
||||
}
|
||||
}
|
||||
|
||||
var generatedClassName = ProjectionExpressionClassNameGenerator.GenerateName(projectable.ClassNamespace, projectable.NestedInClassNames, projectable.MemberName);
|
||||
var generatedFileName = projectable.ClassTypeParameterList is not null ? $"{generatedClassName}-{projectable.ClassTypeParameterList.ChildNodes().Count()}.g.cs" : $"{generatedClassName}.g.cs";
|
||||
|
||||
var lambdaTypeArguments = SyntaxFactory.TypeArgumentList(
|
||||
SyntaxFactory.SeparatedList(
|
||||
projectable.ParametersList?.Parameters.Where(p => p.Type is not null).Select(p => p.Type!)
|
||||
var classSyntax = ClassDeclaration(generatedClassName)
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword)))
|
||||
.WithTypeParameterList(projectable.ClassTypeParameterList)
|
||||
.WithConstraintClauses(projectable.ClassConstraintClauses ?? List<TypeParameterConstraintClauseSyntax>())
|
||||
.AddAttributeLists(
|
||||
AttributeList()
|
||||
.AddAttributes(_editorBrowsableAttribute)
|
||||
)
|
||||
);
|
||||
.AddMembers(
|
||||
MethodDeclaration(
|
||||
GenericName(
|
||||
Identifier("global::System.Linq.Expressions.Expression"),
|
||||
TypeArgumentList(
|
||||
SingletonSeparatedList(
|
||||
(TypeSyntax)GenericName(
|
||||
Identifier("global::System.Func"),
|
||||
GetLambdaTypeArgumentListSyntax(projectable)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
"Expression"
|
||||
)
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.StaticKeyword)))
|
||||
.WithTypeParameterList(projectable.TypeParameterList)
|
||||
.WithConstraintClauses(projectable.ConstraintClauses ?? List<TypeParameterConstraintClauseSyntax>())
|
||||
.WithBody(
|
||||
Block(
|
||||
ReturnStatement(
|
||||
ParenthesizedLambdaExpression(
|
||||
projectable.ParametersList ?? ParameterList(),
|
||||
null,
|
||||
projectable.ExpressionBody
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
resultBuilder.Append($@"
|
||||
namespace EntityFrameworkCore.Projectables.Generated
|
||||
#nullable disable
|
||||
{{
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public static class {generatedClassName}
|
||||
{{
|
||||
public static System.Linq.Expressions.Expression<System.Func<{(lambdaTypeArguments.Arguments.Any() ? $"{lambdaTypeArguments.Arguments}, " : "")}{projectable.ReturnTypeName}>> Expression{(projectable.TypeParameterList?.Parameters.Any() == true ? projectable.TypeParameterList.ToString() : string.Empty)}()");
|
||||
|
||||
if (projectable.ConstraintClauses is not null)
|
||||
var compilationUnit = CompilationUnit()
|
||||
.AddMembers(
|
||||
NamespaceDeclaration(
|
||||
ParseName("EntityFrameworkCore.Projectables.Generated")
|
||||
).AddMembers(classSyntax)
|
||||
)
|
||||
.WithLeadingTrivia(
|
||||
TriviaList(
|
||||
Comment("// <auto-generated/>"),
|
||||
Trivia(NullableDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true))
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
context.AddSource(generatedFileName, SourceText.From(compilationUnit.NormalizeWhitespace().ToFullString(), Encoding.UTF8));
|
||||
|
||||
|
||||
static TypeArgumentListSyntax GetLambdaTypeArgumentListSyntax(ProjectableDescriptor projectable)
|
||||
{
|
||||
foreach (var constraintClause in projectable.ConstraintClauses)
|
||||
var lambdaTypeArguments = TypeArgumentList(
|
||||
SeparatedList(
|
||||
// TODO: Document where clause
|
||||
projectable.ParametersList?.Parameters.Where(p => p.Type is not null).Select(p => p.Type!)
|
||||
)
|
||||
);
|
||||
|
||||
if (projectable.ReturnTypeName is not null)
|
||||
{
|
||||
resultBuilder.Append($@"
|
||||
{constraintClause}");
|
||||
lambdaTypeArguments = lambdaTypeArguments.AddArguments(ParseTypeName(projectable.ReturnTypeName));
|
||||
}
|
||||
|
||||
return lambdaTypeArguments;
|
||||
}
|
||||
|
||||
resultBuilder.Append($@"
|
||||
{{
|
||||
return {projectable.ParametersList} =>
|
||||
{projectable.Body};
|
||||
}}
|
||||
}}
|
||||
}}");
|
||||
|
||||
|
||||
context.AddSource($"{generatedClassName}.g.cs", SourceText.From(resultBuilder.ToString(), Encoding.UTF8));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFrameworkVersion)</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -12,6 +12,19 @@ namespace EntityFrameworkCore.Projectables.Extensions
|
||||
{
|
||||
public static class TypeExtensions
|
||||
{
|
||||
public static string GetSimplifiedTypeName(this Type type)
|
||||
{
|
||||
var name = type.Name;
|
||||
|
||||
var backtickIndex = name.IndexOf("`");
|
||||
if (backtickIndex != -1)
|
||||
{
|
||||
name = name.Substring(0, backtickIndex);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> GetNestedTypePath(this Type type)
|
||||
{
|
||||
if (type.IsNested && type.DeclaringType is not null)
|
||||
|
||||
+25
-1
@@ -29,16 +29,40 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
{
|
||||
stringBuilder.Append(namespaceName?.Replace('.', '_'));
|
||||
stringBuilder.Append('_');
|
||||
var arity = 0;
|
||||
|
||||
if (nestedInClassNames is not null)
|
||||
{
|
||||
|
||||
foreach (var className in nestedInClassNames)
|
||||
{
|
||||
stringBuilder.Append(className);
|
||||
var arityCharacterIndex = className.IndexOf('`');
|
||||
if (arityCharacterIndex is -1)
|
||||
{
|
||||
stringBuilder.Append(className);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
arity += int.Parse(className.Substring(arityCharacterIndex + 1));
|
||||
#else
|
||||
arity += int.Parse(className.AsSpan().Slice(arityCharacterIndex + 1));
|
||||
#endif
|
||||
stringBuilder.Append(className, 0, arityCharacterIndex);
|
||||
}
|
||||
|
||||
stringBuilder.Append('_');
|
||||
}
|
||||
|
||||
}
|
||||
stringBuilder.Append(memberName);
|
||||
|
||||
if (arity > 0)
|
||||
{
|
||||
stringBuilder.Append('`');
|
||||
stringBuilder.Append(arity);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EntityFrameworkCore.Projectables.Extensions;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
|
||||
namespace EntityFrameworkCore.Projectables.Services
|
||||
{
|
||||
@@ -14,39 +15,37 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
{
|
||||
public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo)
|
||||
{
|
||||
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 projectableAttribute = projectableMemberInfo.GetCustomAttribute<ProjectableAttribute>()
|
||||
?? throw new InvalidOperationException("Expected member to have a Projectable attribute. None found");
|
||||
|
||||
var genericArguments = projectableMemberInfo switch {
|
||||
MethodInfo methodInfo => methodInfo.GetGenericArguments(),
|
||||
_ => null
|
||||
var expression = projectableAttribute switch {
|
||||
{ UseMemberBody: not null } => GetExpressionFromMemberBody(projectableMemberInfo, projectableAttribute.UseMemberBody),
|
||||
_ => GetExpressionFromGeneratedType(projectableMemberInfo)
|
||||
};
|
||||
|
||||
var expressionFactoryMethod = declaringType.Assembly.GetType(generatedContainingTypeName)
|
||||
?.GetMethods()
|
||||
?.FirstOrDefault();
|
||||
|
||||
if (expressionFactoryMethod is not null)
|
||||
if (expression is null)
|
||||
{
|
||||
if (genericArguments is { Length: > 0 })
|
||||
{
|
||||
expressionFactoryMethod = expressionFactoryMethod.MakeGenericMethod(genericArguments);
|
||||
}
|
||||
var declaringType = projectableMemberInfo.DeclaringType ?? throw new InvalidOperationException("Expected a valid type here");
|
||||
var fullName = string.Join(".", Enumerable.Empty<string>()
|
||||
.Concat(new[] { declaringType.Namespace })
|
||||
.Concat(declaringType.GetNestedTypePath().Select(x => x.Name))
|
||||
.Concat(new[] { projectableMemberInfo.Name }));
|
||||
|
||||
return expressionFactoryMethod.Invoke(null, null) as LambdaExpression ?? throw new InvalidOperationException("Expected lambda");
|
||||
throw new InvalidOperationException($"Unable to resolve generated expression for {fullName}.");
|
||||
}
|
||||
|
||||
var useMemberBody = projectableMemberInfo.GetCustomAttribute<ProjectableAttribute>()?.UseMemberBody;
|
||||
return expression;
|
||||
|
||||
if (useMemberBody is not null)
|
||||
static LambdaExpression? GetExpressionFromMemberBody(MemberInfo projectableMemberInfo, string memberName)
|
||||
{
|
||||
var exprProperty = declaringType.GetProperty(useMemberBody, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
var declaringType = projectableMemberInfo.DeclaringType ?? throw new InvalidOperationException("Expected a valid type here");
|
||||
var exprProperty = declaringType.GetProperty(memberName, BindingFlags.Static | BindingFlags.Instance | 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.Count == 1 &&
|
||||
lambda.Parameters[0].Type == declaringType && lambda.ReturnType == property.PropertyType)
|
||||
{
|
||||
return lambda;
|
||||
@@ -59,18 +58,44 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
return lambda;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var fullName = string.Join(".", Enumerable.Empty<string>()
|
||||
.Concat(new[] { declaringType.Namespace })
|
||||
.Concat(declaringType.GetNestedTypePath().Select(x => x.Name))
|
||||
.Concat(new[] { projectableMemberInfo.Name }));
|
||||
static LambdaExpression? GetExpressionFromGeneratedType(MemberInfo projectableMemberInfo)
|
||||
{
|
||||
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);
|
||||
|
||||
throw new InvalidOperationException($"Unable to resolve generated expression for {fullName}.") {
|
||||
Data = {
|
||||
["GeneratedContainingTypeName"] = generatedContainingTypeName
|
||||
var expressionFactoryType = declaringType.Assembly.GetType(generatedContainingTypeName);
|
||||
|
||||
if (expressionFactoryType is not null)
|
||||
{
|
||||
if (expressionFactoryType.IsGenericTypeDefinition)
|
||||
{
|
||||
expressionFactoryType = expressionFactoryType.MakeGenericType(declaringType.GenericTypeArguments);
|
||||
}
|
||||
|
||||
var expressionFactoryMethod = expressionFactoryType.GetMethod("Expression", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
var methodGenericArguments = projectableMemberInfo switch {
|
||||
MethodInfo methodInfo => methodInfo.GetGenericArguments(),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (expressionFactoryMethod is not null)
|
||||
{
|
||||
if (methodGenericArguments is { Length: > 0 })
|
||||
{
|
||||
expressionFactoryMethod = expressionFactoryMethod.MakeGenericMethod(methodGenericArguments);
|
||||
}
|
||||
|
||||
return expressionFactoryMethod.Invoke(null, null) as LambdaExpression ?? throw new InvalidOperationException("Expected lambda");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user