Assuming that array is an array of integers:
var array = new [] { 1, 2 }
And let's say there is an object name Some, with properties:
public class Some
{
public int Id { get; set;}
}
I need a way to convert:
Expression<Func<Some, bool>> exp = x => array.Contains(x.Id)
expression into:
Expression<Func<Some, bool>> exp = x => x.Id == 1 || x.Id == 2
UPDATE
I already have an extension method on list which generates wanted result from a list: What I am asking is, given expression 1 how could I convert it to expression 2. I don't want to push other team members to use extension instead of normal contains method.
my extension method:
array.SafeContainsExpression<Some, string>(nameof(Some.Id));
and the code:
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);
}
SOLUTION (From accepted answer)
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 }));
}
}