x.TrParent.-6ren">
gpt4 book ai didi

c# - 如何为 .Where(x => x..Select(y => y.id).Intersect(List).Any()) 创建表达式树

转载 作者:行者123 更新时间:2023-11-30 17:45:37 30 4
gpt4 key购买 nike

我正在创建一个接收 Queryable<T> 的方法source,一个带有属性名称/路径的字符串(可以是一个深层属性,例如 "TrParent.DataTypes" 来实现 x => x.TrParent.DataTypes )和 Enumerable<int>它包含我需要相交的值。

基本上我需要动态创建以下查询(我的意思是 <DT_Det_Tr>TrParent.DataTypes 仅在运行时才知道,在示例中 DT_Det_Tr 不是类型而是类):

var _vals = new List<int>();
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any()
);

请记住,前面的查询只是我需要动态实现的示例,我真正需要的是一个表达式树,它创建一个如上所示的谓词,但使用动态类型和深度导航在字符串中指定的属性。

因此,我正在使用此函数为 deep 属性创建表达式:

private static LambdaExpression CreateDelegateExpression<T>(out Type resultingtype, string property, string parameterName = "x")
{
var type = typeof(T);
ParameterExpression param = Expression.Parameter(type, parameterName);
Expression expr = param;
foreach (string prop in property.Split('.'))
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, param);
resultingtype = type;
return lambda;
}

这是我目前的功能:

public static IQueryable<T> Intersect<T>(this IQueryable<T> source, string property, IEnumerable<int> value)
{
//List of ids
var _value = Expression.Constant(value);

//Get delegate expression to the deep property and it's inner type
Type type = null;
var lambda = CreateDelegateExpression<T>(out type, property, "x");
var enumtype = type.GetGenericArguments()[0];

ParameterExpression tpe = Expression.Parameter(enumtype, "y");

Expression propExp = Expression.Property(tpe, enumtype.GetProperty("Id"));

MethodInfo innermethod = typeof(Queryable).GetMethods().Where(x => x.Name == "Select").First();
//Error on next line...
var selectCall = Expression.Call(typeof(Queryable),
"Select",
new Type[] { enumtype, typeof(long) },
lambda,
propExp);
//TODO: Add rest of logic and actually filter the source
return source;
}

var selectCall =我收到错误的行:

No generic method 'Select' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.

我在 SO 和其他网站上阅读了很多,但我无法通过这一部分,我觉得当我到达 .Intersect(List<int>).Any() 时我会遇到更多麻烦。部分所以任何帮助也将是盛大的,谢谢。

最佳答案

经过大量思考、调查和尝试,我想出了一个解决方案。

首先,我制作了一个更简单的目标查询版本(我在问题中使用的静态示例),而不是:

var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any()
);

我做了这个:

var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Any(y => _vals.Contains(y.Id))
);

这更容易转换为表达式(或者至少对我而言),因为它省略了 Select 调用。

我摆脱了我用来创建深度导航属性表达式的方法,并在我的 Intersect 函数中对其进行了简化,这是因为它正在做一些我在这里并不真正需要的工作,而且我需要访问一些我在里面使用的变量,然后我做了这个:

public static IQueryable<T> Intersect<T>(this IQueryable<T> source, string property, IEnumerable<int> value)
{
var type = typeof(T);
var _value = Expression.Constant(value); //List of ids
//Declare parameter for outer lambda
ParameterExpression param = Expression.Parameter(type, "x");

//Outer Lambda
Expression expr = param;
foreach (string prop in property.Split('.')) //Dig for deep property
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}

//Get deep property's type
var enumtype = type.GetGenericArguments()[0];
//Declare parameter for inner lambda
ParameterExpression tpe = Expression.Parameter(enumtype, "y");

//Inner Collection lambda logic
//Property for inner lambda
Expression propExp = Expression.Property(tpe, enumtype.GetProperty("Id"));
//Contains method call .Contains(y.Id)
var containsMethodExp = Expression.Call(typeof(Enumerable), "Contains", new[] { propExp.Type }, _value, propExp);
//Create Expression<Func<enumtype, bool>>
var innerDelegateType = typeof(Func<,>).MakeGenericType(enumtype, typeof(bool));
//Create Inner lambda y => _vals.Contains(y.Id)
var innerFunction = Expression.Lambda(innerDelegateType, containsMethodExp, tpe);
//Get Any method info
var anyMethod = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(enumtype);
//Call Any with inner function .Any(y => _vals.Contains(y.Id))
var outerFunction = Expression.Call(anyMethod, expr, innerFunction);
//Call Where
MethodCallExpression whereCallExpression = Expression.Call
(
typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda<Func<T, bool>>(outerFunction, new ParameterExpression[] { param })
);
//Create and return query
return source.Provider.CreateQuery<T>(whereCallExpression);
}

我希望这可以帮助任何试图开发类似解决方案的人。

一开始使用表达式树可能会非常困难和令人沮丧,但一旦掌握它,它就会成为一个非常强大的工具。

关于c# - 如何为 .Where(x => x.<deep property>.Select(y => y.id).Intersect(List<int>).Any()) 创建表达式树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27345003/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com