C# 如何为.Where(x=>;x.<;deep property>;.Select(y=>;y.id).Intersect(List<;int>;).Any()创建表达式树
我正在创建一个方法,该方法接收一个C# 如何为.Where(x=>;x.<;deep property>;.Select(y=>;y.id).Intersect(List<;int>;).Any()创建表达式树,c#,select,intersect,any,C#,Select,Intersect,Any,我正在创建一个方法,该方法接收一个Queryable源,一个带有属性名/路径的字符串(可以是一个深层属性,例如“TrParent.DataTypes”,以实现此x=>x.TrParent.DataTypes)和Enumerable,其中包含我需要相交的值 基本上,我需要动态创建以下查询(我的意思是和TrParent.DataTypes只在运行时知道,在示例中DT_Det_Tr不是一种类型,而是一个类): var\u vals=new List(); var res=dbContext.Set()
Queryable
源,一个带有属性名/路径的字符串(可以是一个深层属性,例如“TrParent.DataTypes”
,以实现此x=>x.TrParent.DataTypes
)和Enumerable
,其中包含我需要相交的值
基本上,我需要动态创建以下查询(我的意思是
和TrParent.DataTypes
只在运行时知道,在示例中DT_Det_Tr
不是一种类型,而是一个类):
var\u vals=new List();
var res=dbContext.Set()
哪里
(x=>x.TrParent.DataTypes
.选择(t=>t.Id)
.相交(_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;
}
私有静态LambdaExpression CreateDelegateExpression(输出类型resultingtype,字符串属性,字符串参数name=“x”)
{
var类型=类型(T);
ParameterExpression param=Expression.Parameter(类型,参数名称);
表达式expr=param;
foreach(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);
结果类型=类型;
返回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;
}
公共静态IQueryable Intersect(此IQueryable源、字符串属性、IEnumerable值)
{
//ID列表
var _值=表达式常数(值);
//获取deep属性及其内部类型的委托表达式
Type=null;
var lambda=CreateDelegateExpression(输出类型,属性,“x”);
var enumtype=type.GetGenericArguments()[0];
ParameterExpression tpe=表达式.参数(枚举类型,“y”);
Expression propExp=Expression.Property(tpe,enumtype.GetProperty(“Id”);
MethodInfo innermethod=typeof(Queryable).GetMethods()。其中(x=>x.Name==“选择”).First();
//下一行出错。。。
var selectCall=Expression.Call(typeof(Queryable),
“选择”,
新类型[]{enumtype,typeof(long)},
兰姆达,
(xp),;
//TODO:添加其余逻辑并实际过滤源
返回源;
}
在var selectCall=
行中,我得到了一个错误:
类型“System.Linq.Queryable”上的泛型方法“Select”与提供的类型参数和参数不兼容。如果方法是非泛型的,则不应提供类型参数
我在SO和其他网站上读了很多文章,但我无法通过这一部分,我觉得当我进入
.Intersect(List)部分时,我会遇到更多的麻烦。Any()
部分,因此关于这一部分的任何帮助都将是巨大的,谢谢。如果您可以从c#4.0访问dynamic关键字,您可能能够解决以下问题:
var _vals = new List<int>();
var res = dbContext.Set<DT_Det_Tr>()
.Where(obj => { dynamic x = obj;
return x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any();
}
);
var\u vals=new List();
var res=dbContext.Set()
其中(obj=>{dynamic x=obj;
返回x.TrParent.DataTypes
.选择(t=>t.Id)
.相交(_vals)
.Any();
}
);
但我对你想解决的问题的细节了解不够,所以不能肯定 经过深思熟虑、调查和尝试,我想出了一个解决办法 首先,我制作了一个更简单的目标查询(我在问题中使用的静态示例),因此没有:
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any()
);
var res=dbContext.Set()
哪里
(x=>x.TrParent.DataTypes
.选择(t=>t.Id)
.相交(_vals)
.Any()
);
我做了这个:
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Any(y => _vals.Contains(y.Id))
);
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);
}
var res=dbContext.Set()
哪里
(x=>x.TrParent.DataTypes
.Any(y=>_vals.Contains(y.Id))
);
这更容易翻译成表达式(至少对我来说是这样),因为它省略了Select调用
我放弃了创建深度导航属性表达式的方法,并在我的Intersect函数中对其进行了精简,这是因为它正在做一些我在这里并不真正需要的工作,加上我需要访问其中使用的一些变量,然后我做了以下操作:
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Any(y => _vals.Contains(y.Id))
);
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);
}
公共静态IQueryable Intersect(此IQueryable源、字符串属性、IEnumerable值)
{
var类型=类型(T);
var _value=Expression.Constant(value);//ID列表
//为外部lambda声明参数
ParameterExpression param=表达式参数(类型为“x”);
//外Lambda
表达式expr=param;
foreach(property.Split('.')中的字符串prop//Dig for deep属性
{
PropertyInfo pi=type.GetProperty(prop);
expr=Expression.Property(expr,pi);
type=pi.PropertyType;
}
//获取深度属性的类型
var enumtype=type.GetGenericArguments()[0];
//为内部lambda声明参数
ParameterExpression tpe=表达式.参数(枚举类型,“y”);
//内收集lambda逻辑
//内lambda的性质
Expression propExp=Expression.Property(tpe