0

having an array of Expression<Func<T, string>> and a string str

which could have a value like this: { m => m.FirstName, m => m.LastName}

I wanted to generate a Lambda expression that does this:

m => m.FirstName.Contains(str) || m.LastName.Contains(str)

The problem is that when there's more than 1 Expression, each has its lambda parameter (m =>)

so when I call:

Expression.Lambda<Func<T, bool>>(OrExpr, here needs to be just one lambda parameter

(for now the only solution I know is to generate a new Expression.Parameter and new Expression.MakeMemberAccess[] using my new parameter)

buga
  • 852
  • 9
  • 21
  • "or now the only solution I know is to generate a new Expression.Parameter and new Expression.MakeMemberAccess[] using my new parameter" And wher's your problem with that? I'd expected to do exactly that. – MakePeaceGreatAgain Feb 26 '23 at 19:50
  • that's what I would use if I had an array of strings as input `{ "FirstName", "LastName" }`, I thought that since I already have expressions maybe there's a more optimal way – buga Feb 26 '23 at 19:54
  • more optimal regarding what? Expressions are easy and cheap to build, I'm not sure what kind of optimzation you're after here. – MakePeaceGreatAgain Feb 26 '23 at 19:56
  • a more complex example `m => m.Prop1.Name`, now I need to get `Prop.Name` as string and after do `typeof(TModel).GetProperty("Prop1")`, `MakeMemberAccess` 2 times, and I thought all this ^ can be avoided because I already have an expression – buga Feb 26 '23 at 20:03
  • You mean something like this: `Func d = p => p.FirstName.Contains("a") || p.LastName.Contains("o");` – Maciej Los Feb 26 '23 at 21:55
  • For further details, plase see [Expression Trees](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/) – Maciej Los Feb 26 '23 at 22:13
  • Use `ReplacingExpressionVisitor.Replace` (or your own version) to replace the parameter in each property lambda body with the final parameter you want to use for the combined `Expression`. – NetMage Mar 01 '23 at 18:59

1 Answers1

0

Given propFns as an array of Expression<Func<T, string>> and string str, you can do:

var ansParam = propFns[0].Parameters[0];
var newBody = propFns.Select(f => (Expression)Expression.Call(
                                        ReplacingExpressionVisitor.Replace(f.Parameters[0], ansParam, f.Body),
                                        typeof(String).GetMethod("Contains", new[] { typeof(String) }),
                                        Expression.Constant(str))
              ).Aggregate(Expression.OrElse);
var newLambda = Expression.Lambda<Func<TestClass,bool>>(newBody, ansParam);

NOTE: If you are not using EF Core, and don't have ReplacingExpressionVisitor available, you can use your own version:

/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static T Replace<T>(this T orig, Expression from, Expression to) where T : Expression => (T)new ReplaceVisitor(from, to).Visit(orig);

/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
    readonly Expression from;
    readonly Expression to;

    public ReplaceVisitor(Expression from, Expression to) {
        this.from = from;
        this.to = to;
    }

    public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}
NetMage
  • 26,163
  • 3
  • 34
  • 55
  • seems that this requires to install package EntityFramework.Core – buga Mar 05 '23 at 20:15
  • @buga Yes, but you didn't say what LINQ you were using. You can replace `ReplacingExpressionVisitor` with your own class trivially - it is only a about ten lines of code. – NetMage Mar 06 '23 at 20:41
  • Yes, that's what I ended up using, copied the Visitor from Marc Gravell's answer https://stackoverflow.com/a/457328/11783857 – buga Mar 07 '23 at 12:10