Getting an expression tree from inside a method's body - c#

Context:
I'm making an Expression parser, which would take my LINQ queries, and turn them into specific byte arrays. Think ORM for a custom data storage, euhm, thing. I'll use SQL in my examples for familiarity.
class ExpressionParser<T>
{
public string ParseWhere(Expression<Func<T, bool>> predicate)
{
// Takes an expression, follows the expression tree, building an SQL query.
}
}
Example:
Take an example class FooData with a few dummy properties:
class FooData
{
public int Status { get; set; }
public bool Active { get; set; }
}
var parser = new ExpressionParser<FooData>();
var query = parser.ParseWhere(foo => foo.Active && (foo.Status == 3 || foo.Status == 4));
// Builds "WHERE active AND (status = 3 OR status = 4)"
This works great, my parser runs through the expression tree, builds a WHERE statement, and returns it.
Problem:
Now I see that, for example, Active && (Status == 3 || Status == 4) is a special case that will be used all over the whole project. So naturally I extract it to a computed property:
class FooData
{
public int Status { get; set; }
public bool Active { get; set; }
public bool IsSpecialThing => Active && (Status == 3 || Status == 4);
}
var query = parser.ParseWhere(foo => foo.IsSpecialThing);
Should the expression be evaluated, the result would be the same. However, this doesn't work any more. Instead of a full expression tree that I can make a query from, all I get is a tree with one PropertyExpression that tells me nothing.
I tried changing it to a method, adding a [MethodImpl(MethodImplOptions.AggressiveInlining)] attribute, nothing seems to make Expression look inside my method / property.
Question:
Is it possible to make an Expression look deeper - into a property getter / method body? If not - is there an alternative to Expression that would?
If it's not possible at all, what should one do in this case? It would really suck to copy-paste long parts of queries tens (hundreds?) of times in a project.

The problem here is that this:
public bool IsSpecialThing => Active && (Status == 3 || Status == 4);
Is equivalent to this:
public bool IsSpecialThing { get { return Active && (Status == 3 || Status == 4); } }
Note that they're both compiled methods. You can see this because the type is Func<FooData,bool>, rather than Expression<Func<FooData,bool>>. Short answer: No, you can't inspect it*
If you replace your class definition with this:
public class FooData
{
public int Status { get; set; }
public bool Active { get; set; }
public static Expression<Func<FooData, bool>> IsSpecialThing = (foo) => foo.Active && (foo.Status == 3 || foo.Status == 4);
}
You can then use it as follows:
var parser = new ExpressionParser<FooData>();
var query = parser.ParseWhere(FooData.IsSpecialThing);
Note that this raises more difficulties. I'm assuming you'd want to write something like:
ParseWhere(f => f.IsSpecialThing() && f.SomethingElse)
The problem here is that IsSpecialThing is it's own lambda function, with it's own parameters. So it would be equivalent of writing:
ParseWhere(f => (ff => IsSpecialThing(ff)) && f.SomethingElse)
To combat this, you'd need to write a few helper methods which let you AND and OR LambdaExpressions properly:
public class ParameterRewriter<TArg, TReturn> : ExpressionVisitor
{
Dictionary<ParameterExpression, ParameterExpression> _mapping;
public Expression<Func<TArg, TReturn>> Rewrite(Expression<Func<TArg, TReturn>> expr, Dictionary<ParameterExpression, ParameterExpression> mapping)
{
_mapping = mapping;
return (Expression<Func<TArg, TReturn>>)Visit(expr);
}
protected override Expression VisitParameter(ParameterExpression p)
{
if (_mapping.ContainsKey(p))
return _mapping[p];
return p;
}
}
The above will take a mapping between parameters, and replace them in the given expression tree.
Leveraging it:
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var rewrittenRight = RewriteExpression(left, right);
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left.Body, rewrittenRight.Body), left.Parameters);
}
public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var rewrittenRight = RewriteExpression(left, right);
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left.Body, rewrittenRight.Body), left.Parameters);
}
private static Expression<Func<T, bool>> RewriteExpression<T>(Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var mapping = new Dictionary<ParameterExpression, ParameterExpression>();
for (var i = 0; i < left.Parameters.Count; i++)
mapping[right.Parameters[i]] = left.Parameters[i];
var pr = new ParameterRewriter<T, bool>();
var rewrittenRight = pr.Rewrite(right, mapping);
return rewrittenRight;
}
}
What the above essentially does is, if you write this:
Expression<Func<FooData, bool>> a = f => f.Active;
Expression<Func<FooData, bool>> b = g => g.Status == 5;
Expression<Func<FooData, bool>> c = a.AndAlso(b);
Will return you f => f.Active && f.Status == 5 (note how the parameter g was replaced with f.
Putting it all together:
var parser = new ExpressionParser<FooData>();
var result = parser.ParseWhere(FooData.IsSpecialThing.AndAlso(f => f.Status == 6));
*Note it is technically possible to parse the generated IL, but you'll be in for a hell of a time.

Related

Get nested property for ordering from Expression<Func<T,Tkey>> [duplicate]

I'm creating a Validator<T> class. I'm attempting to implement the Linq SelectMany extension methods for my validator to be able to compose expressions using a Linq query and validate the final result even when the underlying values change.
The following test code demonstrates my intent.
var a = 2;
var b = 3;
var va = Validator.Create(() => a, n => n >= 0 && n < 5);
var vb = Validator.Create(() => b, n => n >= 0 && n < 5);
var vc = from ia in va
from ib in vb
select ia + ib;
Debug.Assert(vc.Value == a + b); //2 + 3
Debug.Assert(vc.Value == 5);
Debug.Assert(vc.IsValid == true);
a = 7;
Debug.Assert(vc.Value == a + b); //7 + 3
Debug.Assert(vc.Value == 10);
Debug.Assert(va.IsValid == false);
Debug.Assert(vb.IsValid == true);
Debug.Assert(vc.IsValid == false);
I've seen the following question How do I compose existing Linq Expressions which shows me how to compose two Func<T, bool>'s together using an And expression, but I need to be able to compose functions together in a more, well, functional way.
I have, for example, the following two expressions:
public Expression<Func<T>> ValueExpression { get; private set; }
public Expression<Func<T, bool>> ValidationExpression { get; private set; }
I wish to create a new expression like this:
public Expression<Func<bool>> IsValidExpression
{
get
{
// TODO: Compose expressions rather than compile & invoke.
}
}
More succinctly I'm trying to create these functions:
// Specific case
Func<Expression<Func<T>>, Expression<Func<T, bool>>, Expression<Func<bool>>>
// General case
Func<Expression<Func<X, Y>>, Expression<Func<Y, Z>>, Expression<Func<X, Z>>>
The general case function can be modified to accept different numbers of generic arguments as needed to compose any function.
I've searched Stack Overflow (of course) and the web, but haven't an example that solves this issue.
My code for the Validator<T> class is below.
public class Validator<T>
{
public Validator(Expression<Func<T>> valueFunc,
Expression<Func<T, bool>> validationFunc)
{
this.ValueExpression = valueFunc;
this.ValidationExpression = validationFunc;
}
public Expression<Func<T>> ValueExpression { get; private set; }
public Expression<Func<T, bool>> ValidationExpression { get; private set; }
public T Value { get { return this.ValueExpression.Compile().Invoke(); } }
public bool IsValid { get { return this.IsValidExpression.Compile().Invoke(); } }
public Expression<Func<bool>> IsValidExpression
{
get
{
// TODO: Compose expressions.
}
}
}
My SelectMany extensions contain loads of yucky .Compile().Invoke() which I want to get rid of.
public static Validator<U> SelectMany<T, U>(this Validator<T> #this, Expression<Func<T, Validator<U>>> k)
{
Expression<Func<T>> fvtv = #this.ValueExpression;
Expression<Func<Validator<U>>> fvu = () => k.Compile().Invoke(fvtv.Compile().Invoke());
Expression<Func<U>> fvuv = fvu.Compile().Invoke().ValueExpression;
Expression<Func<U, bool>> fvtiv = u => #this.ValidationExpression.Compile().Invoke(fvtv.Compile().Invoke());
return fvuv.ToValidator(fvtiv);
}
public static Validator<V> SelectMany<T, U, V>(this Validator<T> #this, Expression<Func<T, Validator<U>>> k, Expression<Func<T, U, V>> s)
{
Expression<Func<Validator<U>>> fvu = () => #this.SelectMany(k);
Expression<Func<T>> fvtv = #this.ValueExpression;
Expression<Func<U>> fvuv = fvu.Compile().Invoke().ValueExpression;
Expression<Func<T, bool>> fvtiv = #this.ValidationExpression;
Expression<Func<U, bool>> fvuiv = u => fvu.Compile().Invoke().ValidationExpression.Compile().Invoke(u);
Expression<Func<V>> fvv = () => s.Compile().Invoke(fvtv.Compile().Invoke(), fvuv.Compile().Invoke());
Expression<Func<V, bool>> fvviv = v => fvtiv.Compile().Invoke(fvtv.Compile().Invoke()) && fvuiv.Compile().Invoke(fvuv.Compile().Invoke());
return fvv.ToValidator(fvviv);
}
Thanks in advance!
While dtb's answer works for several scenarios, it is suboptimal as such an expression cannot be used in Entity Framework, as it cannot handle Invoke calls. Unfortunately, to avoid those calls one needs a lot more code, including a new ExpressionVisitor derived class:
static Expression<Func<A, C>> Compose<A, B, C>(Expression<Func<B, C>> f,
Expression<Func<A, B>> g)
{
var ex = ReplaceExpressions(f.Body, f.Parameters[0], g.Body);
return Expression.Lambda<Func<A, C>>(ex, g.Parameters[0]);
}
static TExpr ReplaceExpressions<TExpr>(TExpr expression,
Expression orig,
Expression replacement)
where TExpr : Expression
{
var replacer = new ExpressionReplacer(orig, replacement);
return replacer.VisitAndConvert(expression, nameof(ReplaceExpressions));
}
private class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression From;
private readonly Expression To;
public ExpressionReplacer(Expression from, Expression to)
{
From = from;
To = to;
}
public override Expression Visit(Expression node)
{
return node == From ? To : base.Visit(node);
}
}
This replaces every instance of the first parameter in the first expression with the expression in the second expression. So a call like this:
Compose((Class1 c) => c.StringProperty, (Class2 c2) => c2.Class1Property
Would yield the expression (Class2 c2) => c2.Class1Property.StringProperty.
The equivalent of Haskell's function composition operator
(.) :: (b->c) -> (a->b) -> (a->c)
f . g = \ x -> f (g x)
would in C# probably be something like
static Expression<Func<A, C>> Compose<A, B, C>(
Expression<Func<B, C>> f,
Expression<Func<A, B>> g)
{
var x = Expression.Parameter(typeof(A));
return Expression.Lambda<Func<A, C>>(
Expression.Invoke(f, Expression.Invoke(g, x)), x);
}
Is this what you're looking for?
Example:
Compose<int, int, string>(y => y.ToString(), x => x + 1).Compile()(10); // "11"

Linq parametrizable group by with 2 properties

I'm trying to do a LinQ request with a group by where a parameter is parametrizable by an Expression ( (Expression<Func<CompanyModel,TKey>> myGroupingProperty) and the other one is hard coded. But even if my code compile I get an error that linq does not support lambdas. Would you have any idea how to do this request?
Here is the code sample:
public List<timelineResult> getTimelinebyCompany<TKey>(Expression<Func<CompanyModel,TKey>> myGroupingProperty, Filter item)
{
int cntToBeSureThatTheQueryExecuteAtLeastOneTime = 0;
List<timelineResult> toto = new List<timelineResult>();
using (var db = new fintechDbContext())
{
while (cntToBeSureThatTheQueryExecuteAtLeastOneTime == 0)
{
toto = (from p in db.companyDBSET
select p).GroupBy(p=> new {p.Founded_Year, myGroupingProperty})
.Select(o => new timelineResult{ year = o.Key.Founded_Year, cluster = o.myGroupingProperty.ToString(), count = o.Count() })
.OrderBy(o => o.year).ToList();
cntToBeSureThatTheQueryExecuteAtLeastOneTime++;
}
}
return toto;
}
What you are seeking for is doable, but not the way you tried because the passed lambda expression cannot be used directly inside another lambda expression.
You should start first by creating a generic class to hold the grouping key. It's similar to system provided Tuple, but has parameterless constructor and simple property get/setters to conform to the EF projection rules:
public class GroupKey<K1, K2>
{
public K1 Key1 { get; set; }
public K2 Key2 { get; set; }
}
Then you need to build dynamically lambda expression like this
Expression<Func<T, K1, K2>> keySelector = x =>
new GroupKey<K1, K2> { Key1 = x.Prop1, Key2 = x.Prop2 };
In order to do that, you'll need some Expression helpers:
public static class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
and you can encapsulate the grouping part in a custom extension method:
public static class QueryableExtensions
{
public static IQueryable<IGrouping<GroupKey<K1, K2>, T>> GroupByPair<T, K1, K2>(this IQueryable<T> source, Expression<Func<T, K1>> keySelector1, Expression<Func<T, K2>> keySelector2)
{
var parameter = keySelector1.Parameters[0];
var key1 = keySelector1.Body;
var key2 = keySelector2.Body.ReplaceParameter(keySelector2.Parameters[0], parameter);
var keyType = typeof(GroupKey<K1, K2>);
var keySelector = Expression.Lambda<Func<T, GroupKey<K1, K2>>>(
Expression.MemberInit(
Expression.New(keyType),
Expression.Bind(keyType.GetProperty("Key1"), key1),
Expression.Bind(keyType.GetProperty("Key2"), key2)),
parameter);
return source.GroupBy(keySelector);
}
}
Finally, the essential part of your method becomes like this:
toto = db.companyDBSET
.GroupByPair(p => p.Founded_Year, myGroupingProperty)
.Select(g => new timelineResult
{
year = g.Key.Key1,
cluster = g.Key.Key2.ToString(),
count = g.Count()
})
.OrderBy(o => o.year)
.ToList();

Linq to sql avoid where in

Let's say I have a list of ids passed by some other part in my program. I need to perform a linq-to-sql query on the DB (to lookup some other data). I want to do it based on the ids that identify my items in scope.
If id list was small enough, I would use "contains" in the linq expression. But it isn't. The problem is that the resulting SQL uses a "where in (id1,id2...)" clause which is getting too large.
My question is: how could I avoid that?
Of course I don't want an in-memory query, that would be easy - I want to let the DB do the job for performane reasons :)
Update: An example linq:
//this is passed in my program from somewhere
int[] myList = new int[]{0,1,2,3,4}; is a list of ids.
//this linq-to-sql will end up in a "where in" clause
myDataTable.Where(a => myList.Contains(a.ID));
We use this extension:
public static IQueryable<T> WhereValueIn<T>(this IQueryable<T> query,
Expression<Func<T, long>> propertyAccessor, ICollection<long> valuesList)
{
return query.Where(PrepareContainsPredicate(propertyAccessor, valuesList));
}
private static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
Expression<Func<T1, T2>> first,
Expression<Func<T2, T3>> second)
{
var param = Expression.Parameter(typeof(T1), #"param");
var newFirst = new ReplaceVisitor(first.Parameters.First(), param).Visit(first.Body);
var newSecond = new ReplaceVisitor(second.Parameters.First(), newFirst).Visit(second.Body);
return Expression.Lambda<Func<T1, T3>>(newSecond, param);
}
public static Expression<Func<T, bool>> PrepareContainsPredicate<T>(Expression<Func<T, long>> idAccessor, ICollection<long> entityIds)
{
if (entityIds.Count == 1)
{
var cardId = entityIds.First();
return Combine(idAccessor, x => x == cardId);
}
if (entityIds.Count == 2)
{
var card1Id = entityIds.ElementAt(0);
var card2Id = entityIds.ElementAt(1);
return Combine(idAccessor, x => x == card1Id || x == card2Id);
}
if (entityIds.Count == 3)
{
var card1Id = entityIds.ElementAt(0);
var card2Id = entityIds.ElementAt(1);
var card3Id = entityIds.ElementAt(2);
return Combine(idAccessor, x => x == card1Id || x == card2Id || x == card3Id);
}
return Combine(idAccessor, x => entityIds.Contains(x));
}
private sealed class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression _from;
private readonly Expression _to;
public ReplaceVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression node)
{
return node == _from ? _to : base.Visit(node);
}
}
And call this:
var ids = new long[] {1L, 2L};
var result = query.WhereValueIn(o => o.Id, ids);
So, if you have small collection, LINQ will generate code with Id = 1 OR Id = 2, other way it will generate IN statement

Dynamic If Statement - Complex Filtering

I have a C# project that allows users to create filters on data using Regular Expressions. They can add as many filters as they want. Each filter consists of a Field and a regular expression that the user types in.
Right now it works with all AND logic. I loop through each filter and if it doesn't match I set skip = true and break out of the loop. Then if skip == true I skip that record and don't include it. So each and every filter must match in order for the field to be included.
However, now they want the ability to add more complex logic rules. So for example if they have created 4 filter rules. They want to be able to specify:
1 AND 2 AND (3 OR 4)
or they may want to specify
1 OR 2 OR 3 OR 4
or they may want to specify
(1 AND 2 AND 3) OR 4
and so on...I think you get the point.
I have added a textbox where they can type in the logic that they want.
I have been racking my brain and I am stumped on how to make this work. My only conclusion is to somehow be able to create a dynamic IF statement that is based on the text they type into the textbox but I don't know if that is even possible.
It seems like there should be an easy way to do this but I can't figure it out. If anyone could help me, I would really appreciate it.
Thanks!
Here's a full test that works as you want it with regular expressions and AND, OR and brackets. Note that this only supports the operators AND and OR and parentheses ( and ) and expects the input to be somewhat well formed (regular expressions must not have spaces). The parsing can be improved, the idea remains the same.
Here is the overall test:
var input = ".* AND [0-9]+ AND abc OR (abc AND def)";
var rpn = ParseRPN(input);
var test = GetExpression(new Queue<string>(rpn.Reverse())).Compile();
test("abc"); // false
test("abc0"); // true
test("abcdef"); // true
Here is the parsing to reverse polish notation:
public Queue<string> ParseRPN(string input)
{
// improve the parsing into tokens here
var output = new Queue<string>();
var ops = new Stack<string>();
input = input.Replace("(","( ").Replace(")"," )");
var split = input.Split(' ');
foreach (var token in split)
{
if (token == "AND" || token == "OR")
{
while (ops.Count > 0 && (ops.Peek() == "AND" || ops.Peek() == "OR"))
{
output.Enqueue(ops.Pop());
}
ops.Push(token);
}
else if (token == "(") ops.Push(token);
else if (token == ")")
{
while (ops.Count > 0 && ops.Peek() != "(")
{
output.Enqueue(ops.Pop());
}
ops.Pop();
}
else output.Enqueue(token); // it's a number
}
while (ops.Count > 0)
{
output.Enqueue(ops.Pop());
}
return output;
}
And the magic GetExpression:
public Expression<Func<string,bool>> GetExpression(Queue<string> input)
{
var exp = input.Dequeue();
if (exp == "AND") return GetExpression(input).And(GetExpression(input));
else if (exp == "OR") return GetExpression(input).Or(GetExpression(input));
else return (test => Regex.IsMatch(test, exp));
}
Note this does rely on PredicateBuilder, but the extension functions used are here in there completeness:
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T> () { return f => true; }
public static Expression<Func<T, bool>> False<T> () { return f => false; }
public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
}
Something like following - define operation classes to represent binary operation and build your tree:
interface IFilter
{
bool Filter(Record r);
}
class SimpleFilter : IFilter
{
bool Filter(Record r)
{
return RegExpMatch(r);
}
}
class AndFilter : IFilter
{
public AndFilter(IFilter left, IFilter right) {}
bool Filter(Record r)
{
return left.Filter(r) && right.Filter(r);
}
}
class OrFilter : IFilter
{
public OrFilter(IFilter left, IFilter right) {}
bool Filter(Record r)
{
return left.Filter(r) || right.Filter(r);
}
}
First step is to parse your expression into an abstract syntax tree. To do that, you can use the shunting-yard algorithm.
After you have done that, you can recursively evaluate the tree, using virtual methods or an interface. For example, you can have a SimpleNode class, that represents a simple expression (like 1 in your example) and can evaluate it. Then you have AndNode that represents AND operation and has two child nodes. It evaluates the child nodes and returns whether both succeeded.
This explanation of the Specification pattern (with example code) should help.
http://en.wikipedia.org/wiki/Specification_pattern#C.23
There may be libraries to do this sort of thing for you, but in the past, I've hand-rolled something along these lines, based around using Predicate;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
public enum CombineOptions
{
And,
Or,
}
public class FilterExpression
{
public string Filter { get; set; }
public CombineOptions Options { get; private set; }
public FilterExpression(string filter, CombineOptions options)
{
this.Filter = filter;
this.Options = options;
}
}
public static class PredicateExtensions
{
public static Predicate<T> And<T>
(this Predicate<T> original, Predicate<T> newPredicate)
{
return t => original(t) && newPredicate(t);
}
public static Predicate<T> Or<T>
(this Predicate<T> original, Predicate<T> newPredicate)
{
return t => original(t) || newPredicate(t);
}
}
public static class ExpressionBuilder
{
public static Predicate<string> BuildExpression(IEnumerable<FilterExpression> filterExpressions)
{
Predicate<string> predicate = (delegate
{
return true;
});
foreach (FilterExpression expression in filterExpressions)
{
string nextFilter = expression.Filter;
Predicate<string> nextPredicate = (o => Regex.Match(o, nextFilter).Success);
switch (expression.Options)
{
case CombineOptions.And:
predicate = predicate.And(nextPredicate);
break;
case CombineOptions.Or:
predicate = predicate.Or(nextPredicate);
break;
}
}
return predicate;
}
}
class Program
{
static void Main(string[] args)
{
FilterExpression f1 = new FilterExpression(#"data([A-Za-z0-9\-]+)$", CombineOptions.And);
FilterExpression f2 = new FilterExpression(#"otherdata([A-Za-z0-9\-]+)$", CombineOptions.And);
FilterExpression f3 = new FilterExpression(#"otherdata([A-Za-z0-9\-]+)$", CombineOptions.Or);
// result will be false as "data1" does not match both filters
Predicate<string> pred2 = ExpressionBuilder.BuildExpression(new[] { f1, f2 });
bool result = pred2.Invoke("data1");
// result will be true as "data1" matches 1 of the 2 Or'd filters
Predicate<string> pred3 = ExpressionBuilder.BuildExpression(new[] { f1, f3 });
result = pred3.Invoke("data1");
}
}
}
All you'll need to do now is parse the 'brackets' to determine the order in which your FilterExpressions should be sent to the BuildExpression method. You might need to tweak it for more complex expressions, but hopefully this helps.

How do I compose Linq Expressions? ie Func<Exp<Func<X, Y>>, Exp<Func<Y, Z>>, Exp<Func<X, Z>>>

I'm creating a Validator<T> class. I'm attempting to implement the Linq SelectMany extension methods for my validator to be able to compose expressions using a Linq query and validate the final result even when the underlying values change.
The following test code demonstrates my intent.
var a = 2;
var b = 3;
var va = Validator.Create(() => a, n => n >= 0 && n < 5);
var vb = Validator.Create(() => b, n => n >= 0 && n < 5);
var vc = from ia in va
from ib in vb
select ia + ib;
Debug.Assert(vc.Value == a + b); //2 + 3
Debug.Assert(vc.Value == 5);
Debug.Assert(vc.IsValid == true);
a = 7;
Debug.Assert(vc.Value == a + b); //7 + 3
Debug.Assert(vc.Value == 10);
Debug.Assert(va.IsValid == false);
Debug.Assert(vb.IsValid == true);
Debug.Assert(vc.IsValid == false);
I've seen the following question How do I compose existing Linq Expressions which shows me how to compose two Func<T, bool>'s together using an And expression, but I need to be able to compose functions together in a more, well, functional way.
I have, for example, the following two expressions:
public Expression<Func<T>> ValueExpression { get; private set; }
public Expression<Func<T, bool>> ValidationExpression { get; private set; }
I wish to create a new expression like this:
public Expression<Func<bool>> IsValidExpression
{
get
{
// TODO: Compose expressions rather than compile & invoke.
}
}
More succinctly I'm trying to create these functions:
// Specific case
Func<Expression<Func<T>>, Expression<Func<T, bool>>, Expression<Func<bool>>>
// General case
Func<Expression<Func<X, Y>>, Expression<Func<Y, Z>>, Expression<Func<X, Z>>>
The general case function can be modified to accept different numbers of generic arguments as needed to compose any function.
I've searched Stack Overflow (of course) and the web, but haven't an example that solves this issue.
My code for the Validator<T> class is below.
public class Validator<T>
{
public Validator(Expression<Func<T>> valueFunc,
Expression<Func<T, bool>> validationFunc)
{
this.ValueExpression = valueFunc;
this.ValidationExpression = validationFunc;
}
public Expression<Func<T>> ValueExpression { get; private set; }
public Expression<Func<T, bool>> ValidationExpression { get; private set; }
public T Value { get { return this.ValueExpression.Compile().Invoke(); } }
public bool IsValid { get { return this.IsValidExpression.Compile().Invoke(); } }
public Expression<Func<bool>> IsValidExpression
{
get
{
// TODO: Compose expressions.
}
}
}
My SelectMany extensions contain loads of yucky .Compile().Invoke() which I want to get rid of.
public static Validator<U> SelectMany<T, U>(this Validator<T> #this, Expression<Func<T, Validator<U>>> k)
{
Expression<Func<T>> fvtv = #this.ValueExpression;
Expression<Func<Validator<U>>> fvu = () => k.Compile().Invoke(fvtv.Compile().Invoke());
Expression<Func<U>> fvuv = fvu.Compile().Invoke().ValueExpression;
Expression<Func<U, bool>> fvtiv = u => #this.ValidationExpression.Compile().Invoke(fvtv.Compile().Invoke());
return fvuv.ToValidator(fvtiv);
}
public static Validator<V> SelectMany<T, U, V>(this Validator<T> #this, Expression<Func<T, Validator<U>>> k, Expression<Func<T, U, V>> s)
{
Expression<Func<Validator<U>>> fvu = () => #this.SelectMany(k);
Expression<Func<T>> fvtv = #this.ValueExpression;
Expression<Func<U>> fvuv = fvu.Compile().Invoke().ValueExpression;
Expression<Func<T, bool>> fvtiv = #this.ValidationExpression;
Expression<Func<U, bool>> fvuiv = u => fvu.Compile().Invoke().ValidationExpression.Compile().Invoke(u);
Expression<Func<V>> fvv = () => s.Compile().Invoke(fvtv.Compile().Invoke(), fvuv.Compile().Invoke());
Expression<Func<V, bool>> fvviv = v => fvtiv.Compile().Invoke(fvtv.Compile().Invoke()) && fvuiv.Compile().Invoke(fvuv.Compile().Invoke());
return fvv.ToValidator(fvviv);
}
Thanks in advance!
While dtb's answer works for several scenarios, it is suboptimal as such an expression cannot be used in Entity Framework, as it cannot handle Invoke calls. Unfortunately, to avoid those calls one needs a lot more code, including a new ExpressionVisitor derived class:
static Expression<Func<A, C>> Compose<A, B, C>(Expression<Func<B, C>> f,
Expression<Func<A, B>> g)
{
var ex = ReplaceExpressions(f.Body, f.Parameters[0], g.Body);
return Expression.Lambda<Func<A, C>>(ex, g.Parameters[0]);
}
static TExpr ReplaceExpressions<TExpr>(TExpr expression,
Expression orig,
Expression replacement)
where TExpr : Expression
{
var replacer = new ExpressionReplacer(orig, replacement);
return replacer.VisitAndConvert(expression, nameof(ReplaceExpressions));
}
private class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression From;
private readonly Expression To;
public ExpressionReplacer(Expression from, Expression to)
{
From = from;
To = to;
}
public override Expression Visit(Expression node)
{
return node == From ? To : base.Visit(node);
}
}
This replaces every instance of the first parameter in the first expression with the expression in the second expression. So a call like this:
Compose((Class1 c) => c.StringProperty, (Class2 c2) => c2.Class1Property
Would yield the expression (Class2 c2) => c2.Class1Property.StringProperty.
The equivalent of Haskell's function composition operator
(.) :: (b->c) -> (a->b) -> (a->c)
f . g = \ x -> f (g x)
would in C# probably be something like
static Expression<Func<A, C>> Compose<A, B, C>(
Expression<Func<B, C>> f,
Expression<Func<A, B>> g)
{
var x = Expression.Parameter(typeof(A));
return Expression.Lambda<Func<A, C>>(
Expression.Invoke(f, Expression.Invoke(g, x)), x);
}
Is this what you're looking for?
Example:
Compose<int, int, string>(y => y.ToString(), x => x + 1).Compile()(10); // "11"

Categories