mirror of https://github.com/abpframework/abp.git
8 changed files with 140 additions and 10 deletions
@ -0,0 +1,83 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq.Expressions; |
|||
using System.Text; |
|||
using Microsoft.EntityFrameworkCore; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore.DataFiltering; |
|||
|
|||
internal static class FilterExpressionPropertyNameInspector |
|||
{ |
|||
// EF Core stores the registered query filter under the CoreAnnotationNames.QueryFilter annotation.
|
|||
// Since EF Core 9 the value is a QueryFilterCollection (internal) whose elements expose an
|
|||
// Expression property. Older versions stored a bare LambdaExpression. We handle both via duck typing
|
|||
// so the test stays compatible across EF Core versions.
|
|||
public static List<string> GetEfPropertyStringArgs(object annotationValue) |
|||
{ |
|||
return Inspect(annotationValue).Args; |
|||
} |
|||
|
|||
public static InspectionResult Inspect(object annotationValue) |
|||
{ |
|||
var collector = new EfPropertyArgCollector(); |
|||
var dump = new StringBuilder(); |
|||
dump.Append("annotationType=").Append(annotationValue?.GetType().FullName ?? "<null>").AppendLine(); |
|||
|
|||
switch (annotationValue) |
|||
{ |
|||
case LambdaExpression lambda: |
|||
dump.Append("lambda: ").AppendLine(lambda.ToString()); |
|||
collector.Visit(lambda); |
|||
break; |
|||
case IEnumerable enumerable: |
|||
var index = 0; |
|||
foreach (var item in enumerable) |
|||
{ |
|||
if (item == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
dump.Append("[").Append(index++).Append("] itemType=").Append(item.GetType().FullName).AppendLine(); |
|||
var expr = item.GetType().GetProperty("Expression")?.GetValue(item) as Expression |
|||
?? item as Expression; |
|||
if (expr != null) |
|||
{ |
|||
dump.Append(" expr: ").AppendLine(expr.ToString()); |
|||
collector.Visit(expr); |
|||
} |
|||
else |
|||
{ |
|||
dump.AppendLine(" (no Expression property)"); |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
|
|||
return new InspectionResult(collector.Args, dump.ToString()); |
|||
} |
|||
|
|||
public sealed record InspectionResult(List<string> Args, string Dump); |
|||
|
|||
private sealed class EfPropertyArgCollector : ExpressionVisitor |
|||
{ |
|||
public List<string> Args { get; } = new(); |
|||
|
|||
protected override Expression VisitMethodCall(MethodCallExpression node) |
|||
{ |
|||
if (node.Method.DeclaringType == typeof(EF) |
|||
&& node.Method.Name == nameof(EF.Property) |
|||
&& node.Arguments.Count == 2) |
|||
{ |
|||
// The 2nd arg comes from a closure-captured local at filter registration, so it
|
|||
// appears as a MemberExpression rather than a ConstantExpression. Evaluate it.
|
|||
if (Expression.Lambda(node.Arguments[1]).Compile().DynamicInvoke() is string name) |
|||
{ |
|||
Args.Add(name); |
|||
} |
|||
} |
|||
|
|||
return base.VisitMethodCall(node); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue