3

I have the following simple extension class

public static class ExpressionOrExtension
{
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> source, Expression<Func<T, bool>> expression)
    {
        if (source == null)
            return expression;
        return Expression.Or(source, expression);
    }
}

But Expression.Or returns a BinaryExpression - How can I get it to return an Expression<Func<T, bool>> instead?

This is how I am trying to consume the method, using Entity Framework

    public IQueryable<BookVerse> FindByVerseReferences(string bookCode, params VerseReference[] verseReferences)
    {
        Expression<Func<BookVerse, bool>> conditions = null;
        foreach(VerseReference verseReference in verseReferences ?? new VerseReference[0])
        {
            conditions = conditions.Or<BookVerse>(x =>
                    x.BookCode == bookCode
                    && x.Chapter == verseReference.Chapter
                    && x.FirstVerse <= verseReference.LastVerse
                    && x.LastVerse >= verseReference.FirstVerse);
        }
        return MyDbContext.BookVerses.Where(conditions);
    }
Peter Morris
  • 20,174
  • 9
  • 81
  • 146

2 Answers2

5

You need to construct a lambda expression:

var p = Expression.Parameter(typeof(T));
return (Expression<Func<T,bool>>)Expression.Lambda(
    Expression.Or(
        Expression.Invoke(source, p)
    ,   Expression.Invoke(expression, p)
    )
,   p
);

Demo:

Expression<Func<int,bool>> a = x=>x > 5;
Expression<Func<int,bool>> b = x=>x < -5;
var or = Or(a, b);
var f = (Func<int,bool>)or.Compile();
for (int i = -10 ; i <= 10 ; i++) {
    Console.WriteLine("{0} - {1}", i, f(i));
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Depending on what the OP is using expressions for, this may or may not work. I know EF doesn't support this, other LINQ providers may have issues with it as well. –  Apr 01 '17 at 13:06
  • Yes, it's Entity Framework. I have added a use-case source snippet for clarification. – Peter Morris Apr 01 '17 at 13:14
  • I thought the `Expression.Invoke` were unescessary but they actually arent. – CSharpie Apr 01 '17 at 13:16
2

I used the LinqKit nuget package and implemented it like so...

    public IQueryable<BookVerse> FindByVerseReferences(string bookCode, params VerseReference[] verseReferences)
    {
        var predicateBuilder = PredicateBuilder.New<BookVerse>();
        Expression<Func<BookVerse, bool>> predicate = null;
        foreach(VerseReference verseReference in verseReferences ?? new VerseReference[0])
        {
            Expression<Func<BookVerse, bool>> conditions = (x =>
                    x.BookCode == bookCode
                    && x.Chapter == verseReference.Chapter
                    && x.FirstVerse <= verseReference.LastVerse
                    && x.LastVerse >= verseReference.FirstVerse);
            predicate = predicateBuilder.Or(conditions);
        }
        return ObjectSpace.BookVerses.AsExpandable().Where(predicate);
    }
Peter Morris
  • 20,174
  • 9
  • 81
  • 146
  • If you are using LINQKit, the whole question makes no any sense because the functionality in question is provided by LINQKit. – Ivan Stoev Apr 01 '17 at 14:00
  • That's why LinqKit is mentioned in the answer and not in the question, because I used to to solve the problem :) – Peter Morris Apr 01 '17 at 17:32
  • 2
    No, you didn't. The question was about your `Or` extension method, and you "answer" has nothing on common with that question. I mean, the `Or` method in question is still not working :) – Ivan Stoev Apr 01 '17 at 17:40
  • We'll have to agree to disagree – Peter Morris Apr 02 '17 at 08:05
  • @IvanStoev The `Or` method in question can now be implemented by a call to LINQKit's `predicateBuilder.Or`. Because it has now become nothing more than a wrapper, that wrapper has been removed in this answer, but the code in this answer does show how it could have been done. –  Apr 02 '17 at 10:18
  • @hvd My point is that when you use LINQKit, you don't need such custom extension methods because they are provided by LINQKit (extension methods with exactly the same signature). Also as you can see from the usage in the above self answer, it's not simply a matter of wrapping the `Or` implementation - it's combined with other LINQKit constructs like `PredicateBuilder.New` and `AsExpandable()`... – Ivan Stoev Apr 02 '17 at 11:32
  • @hvd ... LINQKit `PredicateBuilder` is known to be incompatible with EF and cannot be used separately - there are other implementations like [universal predicate builder](https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/) that can be used as replacement of the original question which have no such requirements. – Ivan Stoev Apr 02 '17 at 11:32
  • @IvanStoev That's not true. Yes, it may generate an incompatible expression, but it also provides the tools to expand it into an expression that *is* compatible with EF. Either using `AsExpandable()`, as this answer shows, or by calling `Expand()`. The fact that there are gotchas does make it flat out incompatible. The fact that the OP says this is working pretty much rules out the possibility of it being incompatible... –  Apr 02 '17 at 12:11
  • @hvd I really don't understand what was unclear it what I wrote - the `PredicateBuilder` class from LINQKit cannot be used **separately** with EF because it's not compatible. In order someone to use it, they need the whole package in order to have `AsExpandable` / `Expand` methods available. If the OP question was **how can I build dynamically Or predicates*, then the above could have been a valid answer, i.e. *use LINQKit package and do blah-blah...*. But it's different - read the title, so you are advocating answer to a question that has not been asked. Enough on this from me, take care. – Ivan Stoev Apr 02 '17 at 12:23