Search code examples
c#linq-to-sqllambdarepository-patternexpression-trees

C# How to convert an Expression<Func<SomeType>> to an Expression<Func<OtherType>>


I have used C# expressions before based on lamdas, but I have no experience composing them by hand. Given an Expression<Func<SomeType, bool>> originalPredicate, I want to create an Expression<Func<OtherType, bool>> translatedPredicate.

In this case SomeType and OtherType have the same fields, but they are not related (no inheritance and not based on a common interface).

Background: I have a repository implementation based on LINQ to SQL. I project the LINQ to SQL entities to my Model entities, to keep my model in POCO. I want to pass expressions to the repository (as a form of specifications) but they should be based on the model entities. But I can't pass those expressions to the data context, since it expects expressions based on the LINQ to SQL entities.


Solution

  • With Expression, the simplest way is with a conversion expression:

    class Foo {
        public int Value { get; set; }
    }
    class Bar {
        public int Value { get; set; }
    }
    static class Program {
        static void Main() {
            Expression<Func<Foo, bool>> predicate =
                x => x.Value % 2 == 0;
            Expression<Func<Bar, Foo>> convert =
                bar => new Foo { Value = bar.Value };
    
            var param = Expression.Parameter(typeof(Bar), "bar");
            var body = Expression.Invoke(predicate,
                  Expression.Invoke(convert, param));
            var lambda = Expression.Lambda<Func<Bar, bool>>(body, param);
    
            // test with LINQ-to-Objects for simplicity
            var func = lambda.Compile();
            bool withOdd = func(new Bar { Value = 7 }),
                 withEven = func(new Bar { Value = 12 });
        }
    }
    

    Note however that this will be supported differently by different providers. EF might not like it, for example, even if LINQ-to-SQL does.

    The other option is to rebuild the expression tree completely, using reflection to find the corresponding members. Much more complex.