3

I have an Expression where I like to insert one more node. I found this SO question similar question which adds a Property at the end of an Expression. I'm quite new to Expressions and I can't figure out to do it between nodes.

I did this simple console test application to show the problem I have:

public class Program
{
    private static void Main()
    {
        var provider = new Provider<Foo>();
        var foo = new Foo {Label = "myLabel", Owner = "Me"};
        provider.AddData(foo);

        provider.AddExpression(f => f.Label);

        // This writes: f => f.Label
        Console.WriteLine(provider.OriginalExpression.ToString());
        // This should write: f => f.WrappedData.Label
        // At the moment it writes: NULL
        Console.WriteLine(provider.WrapperExpression == null ? "NULL" : provider.WrapperExpression.ToString());

        Console.ReadKey();
    }
}

public class Foo
{
    public string Label { get; set; }
    public string Owner { get; set; }
}

public class Wrapper<T> where T : class
{
    public string Id { get; set; }
    public T WrappedData { get; set; }
}

public class Provider<T> where T : class
{
    public Wrapper<T> _internalWrapper = new Wrapper<T>();

    // The expression for the Property when using T (i.e. Foo)
    public Expression<Func<T, string>> OriginalExpression { get; private set; }
    // The expression for the Property when using _internalWrapper
    public Expression<Func<Wrapper<T>, string>> WrapperExpression { get; private set; }

    public void AddData(T data)
    {
        _internalWrapper = new Wrapper<T> { WrappedData = data, Id = "myId" };
    }

    public void AddExpression(Expression<Func<T, string>> valueExpression)
    {
        OriginalExpression = valueExpression;
        BuildWrapperExpression();
    }

    private void BuildWrapperExpression()
    {
        // HERE IS THE PROBLEM:
        // Here I have to insert the node "WrappedData" in the OriginalExpression and save it as WrapperExpression
        // So {f => f.Label} must result into {f => f.WrappedData.Label}
        // It should work as well for deeper nodes. I.e. when OriginalExpression is something like {f => f.Bar.Prop2.Label}
    }
}

I've already tried a view versions in the BuildWrapperExpression() method but none delivers f => f.WrappedData.Label. I get something like f => Invoke(value (ConsoleApplication1.Provide1+<>c__DisplayClass1[ConsoleApplication1.Foo]).lambda, f.WrappedData) or x => Invoke(f => f.Label, Invoke(e => e.WrappedData, x))

For my further use of the expression is has to be x => x.WrappedData.Label

Thanks a lot guys

Community
  • 1
  • 1
Soko
  • 774
  • 1
  • 7
  • 20

2 Answers2

1

Can't you simply:

private void BuildWrapperExpression()
{
    var lambda = OriginalExpression.Compile();
    WrapperExpression = x => lambda(x.WrappedData);
}

Alternatively, you can implement a ExpressionVistor. Check Marc's answer: https://stackoverflow.com/a/9132775/1386995

Community
  • 1
  • 1
Nikita B
  • 3,303
  • 1
  • 23
  • 41
  • Thx for that but I've tried such things already... Your version prints "x => Invoke(value(ConsoleApplication1.Provider`1+<>c__DisplayClass1[ConsoleApplication1.Foo]).lambda, x.WrappedData)" instead of "x => x.WrappedData.Label". I really need the Expression to be a proper Path... – Soko Sep 26 '13 at 19:17
  • UPDATE: But it seems your link gets me on the right track. I'll see where it goes and let you know – Soko Sep 26 '13 at 20:14
1

I came up with a great solution using the link Nikita provided.

I'm using this new class ExpressionChainer:

public static class ExpressionChainer
{
    public static Expression<Func<TOuter, TInner>> Chain<TOuter, TMiddle, TInner>(
        this Expression<Func<TOuter, TMiddle>> left, Expression<Func<TMiddle, TInner>> right)
    {
        return ChainTwo(left, right);
    }

    public static Expression<Func<TOuter, TInner>> ChainTwo<TOuter, TMiddle, TInner>(
        Expression<Func<TOuter, TMiddle>> left, Expression<Func<TMiddle, TInner>> right)
    {
        var swap = new SwapVisitor(right.Parameters[0], left.Body);
        return Expression.Lambda<Func<TOuter, TInner>>(
               swap.Visit(right.Body), left.Parameters);
    }

    private class SwapVisitor : ExpressionVisitor
    {
        private readonly Expression from, to;
        public SwapVisitor(Expression from, Expression to)
        {
            this.from = from;
            this.to = to;
        }
        public override Expression Visit(Expression node)
        {
            return node == from ? to : base.Visit(node);
        }
    }
}

Then all I have to do in is:

   private void BuildWrapperExpression()
    {
        Expression<Func<Wrapper<T>, T>> expression = x => x.WrappedData;            
        WrapperExpression = expression.Chain(OriginalExpression);
    }

Which gives me x => x.WrappedData.Label as WrapperExpression

Soko
  • 774
  • 1
  • 7
  • 20