使用Contains构建Lambda表达式 [英] Build Lambda Expressions with Contains
问题描述
将简单的linq查询转换为Lambda表达式时遇到问题.
I have a problem converting simple linq query to Lambda Expression.
我的查询如下:
int[] array = List<int> array2 = sql.OfType<Table1>().Select(x=>x.ID).Take(10).ToList();
var result = sql.OfType<Table1>().Where(x => array.Contains(x.ID)).Take(10).ToList();
,最终结果应该是:
static void DynamicSQLQuery<T>(IQueryable<T> sql, string fieldName)
{
List<int> array = sql.OfType<T>().Select(SelectExpression<T>(fieldName)).Take(10).ToList();
var result = sql.OfType<T>().Where(InExpression<T>(fieldName, array)).Take(10).ToList();
}
班级
public class Table1
{
public int ID { get; set; }
public string Name { get; set; }
}
我已经转换了第一个lambda:
I already converted the first lambda:
public static Expression<Func<T, int>> SelectExpression<T>(string fieldName)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression selection = Expression.PropertyOrField(param, fieldName);
var lambdaExp = Expression.Lambda<Func<T, int>>(selection, param);
return lambdaExp;
}
但卡在第二个上:
static Expression<Func<T, bool>> InExpression<T>(string propertyName,IEnumerable<int> array)
{
System.Reflection.MethodInfo containsMethod = typeof(IEnumerable<int>).GetMethod("Contains");
ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression member = Expression.PropertyOrField(param, propertyName);//x.{property}
var constant = Expression.Constant(3);
var body = Expression.GreaterThanOrEqual(member, constant); //x.{property} >= 3 but I need array.Contains(x.{property})
var finalExpression = Expression.Lambda<Func<T, bool>>(body, param);
return finalExpression;
}
有人可以帮助我在InExpression方法中创建Lambda表达式x=>array2.Contains(x.ID)
吗?
Can anyone help me to make the lambda expression x=>array2.Contains(x.ID)
in InExpression method?
此外,我将非常感谢您提供有关创建此类表达式的文章/教程的链接.
Also, I'll be very grateful for some link to article/tutorial about creating these type of expressions.
推荐答案
可能类似于:
static Expression<Func<T, bool>> InExpression<T>(
string propertyName, IEnumerable<int> array)
{
var p = Expression.Parameter(typeof(T), "x");
var contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(x => x.Name == "Contains" && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
var property = Expression.PropertyOrField(p, propertyName);
var body = Expression.Call(contains, Expression.Constant(array), property);
return Expression.Lambda<Func<T, bool>>(body, p);
}
这里的窍门是从简单的编译开始.例如:
The trick here is to start off with something simple that compiles; for example:
using System.Linq;
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
public class C {
static Expression<Func<Foo, bool>> InExpression<T>(
string propertyName,IEnumerable<int> array)
{
return x => array.Contains(x.Id);
}
}
class Foo {
public int Id {get;set;}
}
现在要么编译它,然后在ildasm/reflector中查找,要么(并且更加简单):通过 https://sharplab运行它. IO 指定C#作为输出,这样
Now either compile it and look in ildasm/reflector, or (and much simpler): run that through https://sharplab.io specifying C# as the output, like this
这向您显示了编译器生成的代码:
This shows you the code that the compiler generated:
private static Expression<Func<Foo, bool>> InExpression<T>(string propertyName, IEnumerable<int> array)
{
C.<>c__DisplayClass0_0<T> <>c__DisplayClass0_ = new C.<>c__DisplayClass0_0<T>();
<>c__DisplayClass0_.array = array;
ParameterExpression parameterExpression = Expression.Parameter(typeof(Foo), "x");
Expression arg_77_0 = null;
MethodInfo arg_77_1 = methodof(IEnumerable<!!0>.Contains(!!0));
Expression[] expr_38 = new Expression[2];
expr_38[0] = Expression.Field(Expression.Constant(<>c__DisplayClass0_, typeof(C.<>c__DisplayClass0_0<T>)), fieldof(C.<>c__DisplayClass0_0<T>.array));
Expression[] expr_5F = expr_38;
expr_5F[1] = Expression.Property(parameterExpression, methodof(Foo.get_Id()));
Expression arg_86_0 = Expression.Call(arg_77_0, arg_77_1, expr_5F);
ParameterExpression[] expr_82 = new ParameterExpression[1];
expr_82[0] = parameterExpression;
return Expression.Lambda<Func<Foo, bool>>(arg_86_0, expr_82);
}
请注意,这里需要修正一些内容,但是它可以让我们看到它在做什么-例如,像memberof
和fieldof
之类的东西实际上并不存在,因此,我们需要查看一下它们通过反射上升.
Note that there's a few things in here we need to fixup, but it allows us to see what it is doing - things like memberof
and fieldof
don't actually exist, for example, so we need to look them up via reflection.
上述内容的人性化版本:
A humanized version of the above:
private static Expression<Func<Foo, bool>> InExpression<T>(string propertyName, IEnumerable<int> array)
{
ExpressionState state = new ExpressionState();
state.array = array;
ParameterExpression parameterExpression = Expression.Parameter(typeof(Foo), "x");
MethodInfo contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(x => x.Name == nameof(Enumerable.Contains) && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
Expression[] callArgs = new Expression[2];
callArgs[0] = Expression.Field(Expression.Constant(state, typeof(ExpressionState)), nameof(ExpressionState.array));
callArgs[1] = Expression.Property(parameterExpression, propertyName);
Expression body = Expression.Call(null, contains, callArgs);
ParameterExpression[] parameters = new ParameterExpression[1];
parameters[0] = parameterExpression;
return Expression.Lambda<Func<Foo, bool>>(body, parameters);
}
具有:
class ExpressionState
{
public IEnumerable<int> array;
}
这篇关于使用Contains构建Lambda表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!