Replace Parameter in Lambda Expression

Replace parameter in lambda expression

I would do it this way:

Write a parameter-replacer expression-visitor that manipulates the original expression as follows:

  1. Gets rid of the parameter you don't want entirely from the lambda signature.
  2. Replaces all uses of the parameter with the desired indexer expression.

Here's a quick and dirty sample I whipped up based on my earlier answer on a different question:

public static class ParameterReplacer
{
// Produces an expression identical to 'expression'
// except with 'source' parameter replaced with 'target' expression.
public static Expression<TOutput> Replace<TInput, TOutput>
(Expression<TInput> expression,
ParameterExpression source,
Expression target)
{
return new ParameterReplacerVisitor<TOutput>(source, target)
.VisitAndConvert(expression);
}

private class ParameterReplacerVisitor<TOutput> : ExpressionVisitor
{
private ParameterExpression _source;
private Expression _target;

public ParameterReplacerVisitor
(ParameterExpression source, Expression target)
{
_source = source;
_target = target;
}

internal Expression<TOutput> VisitAndConvert<T>(Expression<T> root)
{
return (Expression<TOutput>)VisitLambda(root);
}

protected override Expression VisitLambda<T>(Expression<T> node)
{
// Leave all parameters alone except the one we want to replace.
var parameters = node.Parameters
.Where(p => p != _source);

return Expression.Lambda<TOutput>(Visit(node.Body), parameters);
}

protected override Expression VisitParameter(ParameterExpression node)
{
// Replace the source with the target, visit other params as usual.
return node == _source ? _target : base.VisitParameter(node);
}
}
}

Usage for your scenario (tested):

var zeroIndexIndexer = Expression.MakeIndex
(Expression.Constant(foos),
typeof(List<Foo>).GetProperty("Item"),
new[] { Expression.Constant(0) });

// .ToString() of the below looks like the following:
// () => (value(System.Collections.Generic.List`1[App.Foo]).Item[0].a
// * value(System.Collections.Generic.List`1[App.Foo]).Item[0].b)
var exp1Clone = ParameterReplacer.Replace<Func<Foo, int>, Func<int>>
(exp0, exp0.Parameters.Single(), zeroIndexIndexer);

Replace parameter type in lambda expression

You need to do a few things for this to work:

  • Replace parameter instance both at the Expression.Lambda and anywhere they appear in the body - and use the same instance for both.
  • Change the lambda's delegate type.
  • Replace the property expressions.

Here's the code, with added generics:

public static Func<TTarget, bool> Convert<TSource, TTarget>(Expression<Func<TSource, bool>> root)
{
var visitor = new ParameterTypeVisitor<TSource, TTarget>();
var expression = (Expression<Func<TTarget, bool>>)visitor.Visit(root);
return expression.Compile();
}

public class ParameterTypeVisitor<TSource, TTarget> : ExpressionVisitor
{
private ReadOnlyCollection<ParameterExpression> _parameters;

protected override Expression VisitParameter(ParameterExpression node)
{
return _parameters?.FirstOrDefault(p => p.Name == node.Name) ??
(node.Type == typeof(TSource) ? Expression.Parameter(typeof(TTarget), node.Name) : node);
}

protected override Expression VisitLambda<T>(Expression<T> node)
{
_parameters = VisitAndConvert<ParameterExpression>(node.Parameters, "VisitLambda");
return Expression.Lambda(Visit(node.Body), _parameters);
}

protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(TSource))
{
return Expression.Property(Visit(node.Expression), node.Member.Name);
}
return base.VisitMember(node);
}
}

Replace parameter into expression

You can walk the expression tree of expr, and replace all occurrences of b with paramA using the approach described in this Q&A: "Combine two lambda expressions with inner expression".

However, if you simply need a lambda expression that uses paramA as its parameter, it is easier to make a lambda that wraps expr instead:

var res = (Expression<Func<string,bool>>)Expression.Lambda(
Expression.Invoke(expr, paramA)
, paramA
);

Replace parameter to point to nested parameter in lambda expression

You already solved the concrete issue, so I can't say if what I'm going to propose you is better/more elegant, but for sure is a bit more generic (removed the concrete types/properties/assumptions), hence can be reused for translating similar expressions from different model types.

Here is the code:

public class ExpressionMap
{
private Dictionary<Type, Type> typeMap = new Dictionary<Type, Type>();
private Dictionary<MemberInfo, Expression> memberMap = new Dictionary<MemberInfo, Expression>();
public ExpressionMap Add<TFrom, TTo>()
{
typeMap.Add(typeof(TFrom), typeof(TTo));
return this;
}
public ExpressionMap Add<TFrom, TFromMember, TTo, TToMember>(Expression<Func<TFrom, TFromMember>> from, Expression<Func<TTo, TToMember>> to)
{
memberMap.Add(((MemberExpression)from.Body).Member, to.Body);
return this;
}
public Expression Map(Expression source) => new MapVisitor { map = this }.Visit(source);

private class MapVisitor : ExpressionVisitor
{
public ExpressionMap map;
private Dictionary<Type, ParameterExpression> parameterMap = new Dictionary<Type, ParameterExpression>();
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(Visit(node.Body), node.Parameters.Select(Map));
}
protected override Expression VisitParameter(ParameterExpression node) => Map(node);
protected override Expression VisitMember(MemberExpression node)
{
var expression = Visit(node.Expression);
if (expression == node.Expression)
return node;
Expression mappedMember;
if (map.memberMap.TryGetValue(node.Member, out mappedMember))
return Visit(mappedMember);
return Expression.PropertyOrField(expression, node.Member.Name);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Object == null && node.Method.IsGenericMethod)
{
// Static generic method
var arguments = Visit(node.Arguments);
var genericArguments = node.Method.GetGenericArguments().Select(Map).ToArray();
var method = node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArguments);
return Expression.Call(method, arguments);
}
return base.VisitMethodCall(node);
}
private Type Map(Type type)
{
Type mappedType;
return map.typeMap.TryGetValue(type, out mappedType) ? mappedType : type;
}
private ParameterExpression Map(ParameterExpression parameter)
{
var mappedType = Map(parameter.Type);
ParameterExpression mappedParameter;
if (!parameterMap.TryGetValue(mappedType, out mappedParameter))
parameterMap.Add(mappedType, mappedParameter = Expression.Parameter(mappedType, parameter.Name));
return mappedParameter;
}
}
}

and the usage for your concrete example:

public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate)
{
var map = new ExpressionMap()
.Add<DomainColour, DtoColour>()
.Add((DomainColour c) => c.People, (DtoColour c) => c.FavouriteColours.Select(fc => fc.Person))
.Add<DomainPerson, DtoPerson>();
var mappedPredicate = ((Expression<Func<DtoColour, bool>>)map.Map(predicate));
return Colours.Where(mappedPredicate.Compile()).Select(c => new DomainColour(c.Name)).ToList();
}

As you can see, it allows you to define a simple mapping from one type to another, and optionally from member of one type to member/expression of another type (as soon as they are compatible) using "fluent" syntax with lambda expressions. The members that have no specified mapping are mapped by name as in the original code.

Once the mappings are defined, the actual processing of course is done by a custom ExpressionVisitor, similar to yours. The difference is that it maps and consolidates ParameterExpressions by type, and also translates every static generic method, thus should work also with Queryable and similar.

How to replace a huge lambda expression with a private method adding a parameter?

private static Action<AuthorizationPolicyBuilder> ConfigBuilderFactory(string param)
{
return builder => builder
.RequireClaim("special_claim", param)
.RequireClaim("claim1", "value1")
...;
}
options.AddPolicy("ExtensivePolicy_A", ConfigBuilderFactory("a"));
options.AddPolicy("ExtensivePolicy_Z", ConfigBuilderFactory("z"));

Replacing parameters in a lambda expression

I used strings, since it was the easisest way for me. You can't manually change the name of the parameter expression (it has the "Name" property, but it is read-only), so you must construct a new expression from pieces. What I did is created a "nameless" parameter (actually, it gets an autogenerated name in this case, which is "Param_0") and then created a new expression almost the same as the old one, but using the new parameter.

public static void Main()
{
String thing = "test";

Expression<Action<String>> expression = c => c.ToUpper();
Delegate del = expression.Compile();
del.DynamicInvoke(thing);

Dictionary<String, Delegate> cache = new Dictionary<String, Delegate>();
cache.Add(GenerateKey(expression), del);

Expression<Action<String>> expression1 = d => d.ToUpper();
var test = cache.ContainsKey(GenerateKey(expression1));
Console.WriteLine(test);

}

public static string GenerateKey(Expression<Action<String>> expr)
{
ParameterExpression newParam = Expression.Parameter(expr.Parameters[0].Type);
Expression newExprBody = Expression.Call(newParam, ((MethodCallExpression)expr.Body).Method);
Expression<Action<String>> newExpr = Expression.Lambda<Action<String>>(newExprBody, newParam);
return newExpr.ToString();
}

Replace a parameter in an expression with a constant

ExpressionVisitor is your friend:

static void Main()
{
Expression<Func<int, int, bool>> before = (x, y) => x * 2 == y + 1;
var after = ReplaceParameter(before, 3);
Console.WriteLine(after);
}
public static Expression<Func<TElement, bool>> ReplaceParameter<TElement>
(
Expression<Func<TElement, TElement, bool>> inputExpression,
TElement element
)
{
var replacer = new Replacer(inputExpression.Parameters[0],
Expression.Constant(element, typeof(TElement)));
var body = replacer.Visit(inputExpression.Body);
return Expression.Lambda<Func<TElement, bool>>(body,
inputExpression.Parameters[1]);
}
class Replacer : ExpressionVisitor
{
private readonly Expression _from, _to;
public Replacer(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression node)
=> node == _from ? _to : base.Visit(node);
}

Note that this does not automatically collapse pure constant expressions, i.e. the code shown results in:

y => ((3 * 2) == (y + 1))

You could however, if you wanted, try looking for BinaryExpression that only has ConstantExpression as inputs, and evaluate the node directly, again inside Replacer.

Changing parameter name in a LambdaExpression just for display

It's quick and dirty, but assuming you're using .NET 4.0 you could create the following:

public class PredicateRewriter
{
public static Expression<Predicate<T>> Rewrite<T>(Expression<Predicate<T>> exp, string newParamName)
{
var param = Expression.Parameter(exp.Parameters[0].Type, newParamName);
var newExpression = new PredicateRewriterVisitor(param).Visit(exp);

return (Expression<Predicate<T>>) newExpression;
}

private class PredicateRewriterVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameterExpression;

public PredicateRewriterVisitor(ParameterExpression parameterExpression)
{
_parameterExpression = parameterExpression;
}

protected override Expression VisitParameter(ParameterExpression node)
{
return _parameterExpression;
}
}
}

And then use it as follows:

var newExp = PredicateRewriter.Rewrite(exp, "b");
newExp.ToString(); // returns "b => (b.First() == 0)" in your case

How to use Lambda expression to replace string parameter

public IEnumerable<Show> ListShows(Func<Show, string> stringFromShow)
{

}

Within that method, use

string str = stringFromShow(show);


Related Topics



Leave a reply



Submit