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 }));
}
}
The conversion can work you can build an expression like so.
Expression<Func<A, bool>> exp2 = Expression.Lambda<Func<A, bool>>(
array.Select(i=>Expression.Equal(Expression.Property(p1,"Id"),Expression.Constant(i))).Aggregate((a,i)=> a == null? i:Expression.OrElse(a,i)),p1);
EDIT This will convert the example but for more generic stuff you need to cover more cases of Expression tree.
var array = new[] { 1, 2 };
Expression<Func<A, bool>> exp = x => array.Contains(x.Id);
Expression<Func<A, bool>> exp2 = x => x.Id == 1 || x.Id == 2;
var p1 = Expression.Parameter(typeof(A));
var exp3 = Expression.Lambda<Func<A, bool>>(ExpressionVisitor.Visit(new []{ exp.Body }.ToList().AsReadOnly(), (m) => {
if (m.NodeType == ExpressionType.Call)
{
var method = (MethodCallExpression)m;
if (method.Method.Name == "Contains" && method.Arguments.Count == 2)
{
var items = Expression.Lambda<Func<object>>(method.Arguments[0]).Compile()();
var prop = ((MemberExpression)method.Arguments[1]);
return ((IEnumerable<int>)items).Select(i => Expression.Equal(Expression.Property(prop.Expression, prop.Member.Name), Expression.Constant(i))).Aggregate((a, i) => a == null ? i : Expression.OrElse(a, i));
}
}
return m;
})[0],exp.Parameters);
var func = exp3.Compile();
Console.WriteLine(func(new A { Id = 1 }));
Console.WriteLine(func(new A { Id = 2 }));
Console.WriteLine(func(new A { Id = 3 }));
Instead of
Expression<Func<Some, bool>> exp = x => array.Contains(x.Id)
You can write:
var filters = array.Select<int, Expression<Func<Some, bool>>>(i =>
x => x.Id == i);
Expression<Func<Some, bool>> exp = filters.OrTheseFiltersTogether();
Using my classic method:
public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(
this IEnumerable<Expression<Func<T, bool>>> filters)
{
Expression<Func<T, bool>> firstFilter = filters.FirstOrDefault();
if (firstFilter == null)
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.OrElse(body, nextBody);
}
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
return result;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.