I have an expression like: Expression<Func<TheObject, int, bool>> myExpression = (myObj, theType) => { myObj.Prop > theType };
I need to dynamically rebuild myExpression to a new expression of type Expression<Func<TheObject, bool>> and replace “theType” parameter from the first expression with a concrete value 123 like:
Expression<Func<TheObject, bool>> myNewExpression = myObj => { myObj.Prop > 123 };
How can I do that?
Br
Philip
With a simple expression replacer it is quite easy:
This is the one I've written for myself... supports simple replaces and multiple replaces.
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
// A simple expression visitor to replace some nodes of an expression
// with some other nodes. Can be used with anything, not only with
// ParameterExpression
public class SimpleExpressionReplacer : ExpressionVisitor
{
public readonly Dictionary<Expression, Expression> Replaces;
public SimpleExpressionReplacer(Expression from, Expression to)
{
Replaces = new Dictionary<Expression, Expression> { { from, to } };
}
public SimpleExpressionReplacer(Dictionary<Expression, Expression> replaces)
{
// Note that we should really clone from and to... But we will
// ignore this!
Replaces = replaces;
}
public SimpleExpressionReplacer(IEnumerable<Expression> from, IEnumerable<Expression> to)
{
Replaces = new Dictionary<Expression, Expression>();
using (var enu1 = from.GetEnumerator())
using (var enu2 = to.GetEnumerator())
{
while (true)
{
bool res1 = enu1.MoveNext();
bool res2 = enu2.MoveNext();
if (!res1 || !res2)
{
if (!res1 && !res2)
{
break;
}
if (!res1)
{
throw new ArgumentException("from shorter");
}
throw new ArgumentException("to shorter");
}
Replaces.Add(enu1.Current, enu2.Current);
}
}
}
public override Expression Visit(Expression node)
{
Expression to;
if (node != null && Replaces.TryGetValue(node, out to))
{
return base.Visit(to);
}
return base.Visit(node);
}
}
Your TheObject
public class TheObject
{
public int Prop { get; set; }
}
Then you only need to replace the 2nd parameter in the body of the expression and rebuild the Expression<>
public class Program
{
public static void Main(string[] args)
{
Expression<Func<TheObject, int, bool>> myExpression = (myObj, theType) => myObj.Prop > theType;
int value = 123;
var body = myExpression.Body;
var body2 = new SimpleExpressionReplacer(myExpression.Parameters[1], Expression.Constant(value)).Visit(body);
Expression<Func<TheObject, bool>> myExpression2 = Expression.Lambda<Func<TheObject, bool>>(body2, myExpression.Parameters[0]);
}
}
Related
I have a problem evaluation conditional lambdaexpressions, using System.Linq.Expressions.
Here the full example Code:
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTree
{
class Program<TType>
{
public TType Data { get; set; }
public void RegisterLambda<TProp>(Expression<Func<TType, TProp>> expression)
{
if (expression.Body.NodeType == ExpressionType.Conditional)
{
ConditionalExpression condExp = (ConditionalExpression)expression.Body;
var condTest = condExp.Test;
var paramExpression = Expression.Parameter(typeof(TType), "x");
var lambda = Expression.Lambda(condTest, paramExpression);
var deleg = lambda.Compile(); // this will raise an InvalidOperationException: variable 'x' of type 'ExpressionTree.DataObject'
// referenced from scope '', but it is not defined
var testResult = deleg.DynamicInvoke(Data);
}
if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
var pi = expression.GetPropertyInfo();
Console.WriteLine(pi.GetValue(Data));
}
if (expression.Body.NodeType == ExpressionType.Constant)
{
ConstantExpression ce = (ConstantExpression)expression.Body;
Console.WriteLine(ce.Value);
}
}
}
class Start
{
static void Main(string[] args)
{
Program<DataObject> P = new Program<DataObject>();
P.Data = new DataObject();
P.Data.DisplayName = "1234567890";
P.RegisterLambda(x => x.DisplayName);
P.RegisterLambda(x => x.DisplayName.Length <= 3 ? "Foobar" : x.DisplayName);
P.RegisterLambda<string>(something => "abc");
}
}
class DataObject
{
public string DisplayName { get; set; }
}
static class Extensions
{
public static PropertyInfo GetPropertyInfo(this LambdaExpression expression)
{
if (expression.Body is UnaryExpression unaryExp)
{
if (unaryExp.Operand is MemberExpression memberExp)
{
return (PropertyInfo)memberExp.Member;
}
}
else if (expression.Body is MemberExpression memberExp)
{
return (PropertyInfo)memberExp.Member;
}
throw new Exception();
}
}
}
While calling the 2nd RegisterLambda, which is a ternery conditional lambda. The execution fails on the Compile()-line. Does anyone have a clue to solve that problem? I have tried many combinations using ParameterExpression
Thanks in advance!
I have the following problem in my lambda expression: I need to rename the property because it will be passed from entity to entity. In other words: I need to use the same expression in more than one query in different entities.
For example:
var expr = x => x.Id == converterId
To be
var expr = x => x.ConverterId == converterId
I have tried to do the following
var oldParam = expr.Parameters[0];
var newParam = Expression.Parameter(oldParam.Type, "ConverterId");
This code replaces x not Id`
This isn't trivial, but can be done my writing (subclassing) an ExpressionVisitor, and overriding VisitMember, making the substitution for a different Expression.Property, but using the original target expression (from the expression in the original lambda)
However, for simple cases, it is probably easier to forget that, and just build the expression-tree manually from first principles, rather than using a lambda.
The following shows both approaches:
using System;
using System.Linq.Expressions;
static class P
{
static void Main()
{
// the compiler-generated expression-tree from the question
Console.WriteLine(Baseline(42));
// build our own epression trees manually
Console.WriteLine(ByName(42, nameof(Foo.Id)));
Console.WriteLine(ByName(42, nameof(Foo.ConverterId)));
// take the compiler-generated expression tree, and rewrite it with a visitor
Console.WriteLine(Convert(Baseline(42), nameof(Foo.Id), nameof(Foo.ConverterId)));
}
static Expression<Func<Foo, bool>> Baseline(int converterId)
{
// note this uses a "captured variable", so the output
// looks uglier than you might expect
return x => x.Id == converterId;
}
static Expression<Func<Foo, bool>> ByName(int converterId, string propertyOrFieldName)
{
var p = Expression.Parameter(typeof(Foo), "x");
var body = Expression.Equal(
Expression.PropertyOrField(p, propertyOrFieldName),
Expression.Constant(converterId, typeof(int))
);
return Expression.Lambda<Func<Foo, bool>>(body, p);
}
static Expression<Func<Foo, bool>> Convert(
Expression<Func<Foo, bool>> lambda, string from, string to)
{
var visitor = new ConversionVisitor(from, to);
return (Expression<Func<Foo, bool>>)visitor.Visit(lambda);
}
class ConversionVisitor : ExpressionVisitor
{
private readonly string _from, _to;
public ConversionVisitor(string from, string to)
{
_from = from;
_to = to;
}
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.Name == _from)
{
return Expression.PropertyOrField(
node.Expression, _to);
}
return base.VisitMember(node);
}
}
}
class Foo
{
public int Id { get; set; }
public int ConverterId { get; set; }
}
I'm trying to modify the expression of a function.
I want to replace a functions input argument with a function that returns the same value.
The idea is to convert: function (Foo foo) { return foo.ToString(); }
with function(Func<Foo> fooProvider) { return fooProvider().ToString() }
Actually I don't even want to call the function passed to us, but directly use the body of the function.
So the real example does not get an Func<Foo> as input, but an Expression<Func<Foo>>.
Example code can be found below.
I assume I get an exception now because I change the signature of the function.
Although that is exactly what I want to do.
Some code:
The program
class Program
{
public static void Run()
{
Expression<Func<int, bool>> isOdd = number => (number % 2) == 1;
Expression<Func<string, int>> parseTextToNumber = text => Convert.ToInt32(text);
var vistor = new ReplaceExpressionVisitor(isOdd.Parameters[0], parseTextToNumber.Body);
var result = vistor.Visit(isOdd);
Expression<Func<string, bool>> textRepresentsOddNumber = result as dynamic;
//Expected something like this:
Expression<Func<string, bool>> expectedResult = text => (Convert.ToInt32(text) % 2) == 1;
}
}
The visitor class
private class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
And the error message:
System.InvalidOperationException: 'When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression'
must return a non - null value of the same type.
Alternatively, override 'VisitLambda' and change it to not visit children of this type.'
After a couple of hours I did found something that works.
PS: Not sure if it is the most generic or best solution there is to this problem.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Run()
{
Expression<Func<int, bool>> isOdd = number => (number % 2) == 0;
Expression<Func<string, int>> parseTextToNumber = text => Convert.ToInt32(text);
var visitor = new ReplaceExpressionVisitor(isOdd.Parameters[0], parseTextToNumber.Body);
var result = Expression.Lambda(visitor.Visit(isOdd.Body), parseTextToNumber.Parameters);
Expression<Func<string, bool>> textRepresentsOddNumber = result as dynamic;
//test:
var code = textRepresentsOddNumber.Compile();
Console.WriteLine(code.DynamicInvoke("0"));//writes false
Console.WriteLine(code.DynamicInvoke("1"));//writes true
}
}
private class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
I would like to extend an expression (Expression<Func<TModel, TProperty>>):
class CustomModel
{
public BlobModel Blob { get; set; }
public class BlobModel
{
public string SubBlob { get; set; }
}
}
var expression = model => model.Blog;
var subBlobExpression = expression.???.SubBlob;
It is possible?
The goal is to use the validation in a shared partialview (ASP.NET MVC project).
The htmlAttributes in the ValidationMessageFor method doesn't work and use the model without Expression either!
Unclear what you exactly want and what limits you have, BUT:
// Taken (with some cuts) from https://stackoverflow.com/a/32007349/613130
// A simple expression visitor to replace some nodes of an expression
// with some other nodes. Can be used with anything, not only with
// ParameterExpression
public class SimpleExpressionReplacer : ExpressionVisitor
{
public readonly Dictionary<Expression, Expression> Replaces;
public SimpleExpressionReplacer(Expression from, Expression to)
{
Replaces = new Dictionary<Expression, Expression> { { from, to } };
}
public override Expression Visit(Expression node)
{
Expression to;
if (node != null && Replaces.TryGetValue(node, out to))
{
return base.Visit(to);
}
return base.Visit(node);
}
}
public static Expression<Func<T, TProp2>> ExtendExpression<T, TProp1, TProp2>(Expression<Func<T, TProp1>> exp1, Expression<Func<TProp1, TProp2>> exp2)
{
Expression body3 = new SimpleExpressionReplacer(exp2.Parameters[0], exp1.Body).Visit(exp2.Body);
Expression<Func<T, TProp2>> exp3 = Expression.Lambda<Func<T, TProp2>>(body3, exp1.Parameters);
return exp3;
}
Use it like:
Expression<Func<CustomModel, CustomModel.BlobModel>> expression = model => model.Blob;
var exp2 = ExtendExpression(expression, x => x.SubBlob);
I use an expression replacer (SimpleExpressionReplacer) to "merge" together the two expresion (expression and x => x.SubBlob).
I have two classes:
public class DalMembershipUser
{
public string UserName { get; set; }
//other members
}
public class MembershipUser
{
public string UserName { get; set; }
//other members
}
I have function:
public IEnumerable<DalMembershipUser> GetMany(Expression<Func<DalMembershipUser, bool>> predicate)
{
//but here i can use only Func<MembershipUser, bool>
//so i made transformation
query = query.Where(ExpressionTransformer<DalMembershipUser,MembershipUser>.Tranform(predicate));
}
Current implementation:
public static class ExpressionTransformer<TFrom, TTo>
{
public class Visitor : ExpressionVisitor
{
private ParameterExpression _targetParameterExpression;
public Visitor(ParameterExpression parameter)
{
_targetParameterExpression = parameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return _targetParameterExpression;
}
}
public static Expression<Func<TTo, bool>> Tranform(Expression<Func<TFrom, bool>> expression)
{
ParameterExpression parameter = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name);
Expression body = expression.Body;
new Visitor(parameter).Visit(expression.Body);
return Expression.Lambda<Func<TTo, bool>>(body, parameter);
}
}
//Somewhere: .GetMany(u => u.UserName == "username");
Exception: Property 'System.String UserName' is not defined for type 'MembershipUser'
at line: new Visitor(parameter).Visit(expression.Body);
Finally it work. But still don't understand why clear parameters creation:
Expression.Parameter(typeof(TTo), from.Parameters[i].Name); not work and need to extract.
public static class ExpressionHelper
{
public static Expression<Func<TTo, bool>> TypeConvert<TFrom, TTo>(
this Expression<Func<TFrom, bool>> from)
{
if (from == null) return null;
return ConvertImpl<Func<TFrom, bool>, Func<TTo, bool>>(from);
}
private static Expression<TTo> ConvertImpl<TFrom, TTo>(Expression<TFrom> from)
where TFrom : class
where TTo : class
{
// figure out which types are different in the function-signature
var fromTypes = from.Type.GetGenericArguments();
var toTypes = typeof(TTo).GetGenericArguments();
if (fromTypes.Length != toTypes.Length)
throw new NotSupportedException("Incompatible lambda function-type signatures");
Dictionary<Type, Type> typeMap = new Dictionary<Type, Type>();
for (int i = 0; i < fromTypes.Length; i++)
{
if (fromTypes[i] != toTypes[i])
typeMap[fromTypes[i]] = toTypes[i];
}
// re-map all parameters that involve different types
Dictionary<Expression, Expression> parameterMap = new Dictionary<Expression, Expression>();
ParameterExpression[] newParams = GenerateParameterMap<TFrom>(from, typeMap, parameterMap);
// rebuild the lambda
var body = new TypeConversionVisitor<TTo>(parameterMap).Visit(from.Body);
return Expression.Lambda<TTo>(body, newParams);
}
private static ParameterExpression[] GenerateParameterMap<TFrom>(
Expression<TFrom> from,
Dictionary<Type, Type> typeMap,
Dictionary<Expression, Expression> parameterMap
)
where TFrom : class
{
var newParams = new ParameterExpression[from.Parameters.Count];
for (int i = 0; i < newParams.Length; i++)
{
Type newType;
if (typeMap.TryGetValue(from.Parameters[i].Type, out newType))
{
parameterMap[from.Parameters[i]] = newParams[i] = Expression.Parameter(newType, from.Parameters[i].Name);
}
}
return newParams;
}
class TypeConversionVisitor<T> : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> parameterMap;
public TypeConversionVisitor(Dictionary<Expression, Expression> parameterMap)
{
this.parameterMap = parameterMap;
}
protected override Expression VisitParameter(ParameterExpression node)
{
// re-map the parameter
Expression found;
if (!parameterMap.TryGetValue(node, out found))
found = base.VisitParameter(node);
return found;
}
public override Expression Visit(Expression node)
{
LambdaExpression lambda = node as LambdaExpression;
if (lambda != null && !parameterMap.ContainsKey(lambda.Parameters.First()))
{
return new TypeConversionVisitor<T>(parameterMap).Visit(lambda.Body);
}
return base.Visit(node);
}
protected override Expression VisitMember(MemberExpression node)
{
// re-perform any member-binding
var expr = Visit(node.Expression);
if (expr.Type != node.Type)
{
if (expr.Type.GetMember(node.Member.Name).Any())
{
MemberInfo newMember = expr.Type.GetMember(node.Member.Name).Single();
return Expression.MakeMemberAccess(expr, newMember);
}
}
return base.VisitMember(node);
}
}
}
You need to use the result expression returned by the Visit method.
Just change:
Expression body = expression.Body;
new Visitor(parameter).Visit(expression.Body);
by
Expression body = new Visitor(parameter).Visit(expression.Body);