【发布时间】:2017-06-16 12:49:14
【问题描述】:
假设该数组是一个整数数组:
var array = new [] { 1, 2 }
假设有一个对象名称Some,带有属性:
public class Some
{
public int Id { get; set;}
}
我需要一种转换方式:
Expression<Func<Some, bool>> exp = x => array.Contains(x.Id)
表达式为:
Expression<Func<Some, bool>> exp = x => x.Id == 1 || x.Id == 2
更新
我已经在列表上有一个扩展方法,它从列表中生成想要的结果:我要问的是,给定表达式 1,我如何将其转换为表达式 2。我不想推动其他团队成员使用扩展而不是普通的 contains 方法。
我的扩展方法:
array.SafeContainsExpression<Some, string>(nameof(Some.Id));
和代码:
public static Expression<Func<TModel, bool>> SafeContainsExpression<TModel, TValue>(
this IEnumerable<TValue> list, string propertyName)
{
var argParam = Expression.Parameter(typeof(TModel), "x");
var selector = Expression.Property(argParam, propertyName);
Expression left = null;
foreach (var value in list)
{
var valueExpression = Expression.Constant(value, typeof(TValue));
var right = Expression.Equal(selector, valueExpression);
if (left == null)
left = right;
left = Expression.OrElse(left, right);
}
return Expression.Lambda<Func<TModel, bool>>(left, argParam);
}
解决方案(来自已接受的答案)
public class SafeExpressionsVisitor : LinqKit.ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.Name == "Contains" && m.Arguments.Count == 2)
{
var list = Expression.Lambda<Func<IEnumerable>>(m.Arguments[0]).Compile()();
var propertyExpression = (MemberExpression)m.Arguments[1];
Expression left = null;
foreach (var value in list)
{
var valueExpression = Expression.Constant(value);
var right = Expression.Equal(propertyExpression, valueExpression);
if (left == null)
{
left = right;
continue;
}
left = Expression.OrElse(left, right);
}
return left;
}
return base.VisitMethodCall(m);
}
}
public class ExpressionTests
{
[Fact]
public void Shoul_Convert_With_Visitor()
{
var array = new[] { 1, 2 };
Expression<Func<A, bool>> exp = x => array.Contains(x.Id);
var safeExp = Expression.Lambda<Func<A, bool>>(
new SafeExpressionsVisitor().Visit(exp.Body),
exp.Parameters);
var func = safeExp.Compile();
Assert.True(func(new A { Id = 1 }));
Assert.True(func(new A { Id = 2 }));
Assert.False(func(new A { Id = 3 }));
}
}
【问题讨论】:
-
转换是什么意思?为什么不换?
-
为什么?如果这是某种优化传递,最好用
HashSet.Contains替换它。 -
@SergeyBerezovskiy 这是一个伪代码,数组的值在运行时改变。我认为我需要一个
ExpressionVisitor来获取数组的值,并为每个值从Expression.Equal创建表达式并将它们与Expression.OrElse链接起来。但我不知道该怎么做。 -
@JeroenMostert 我想让它查询不支持
contains方法的 RavenDB
标签: c# linq expression