4

Suppose I have a complex lambda expression as follows:

x => x.A.HasValue || (x.B.HasValue && x.C == q) || (!x.C.HasValue && !x.A.HasValue) || //...expression goes on

I want to use this as an Expression<Func<T,bool> in (e.g. Linq-To-Entities) Queryable.Where method. I also want to use it in the Enumerable.Where method, but the Where method only accepts a Func<T,bool>, not an Expression<Func<T,bool>.

The lambda syntax itself can be used to generate either an Expression<Func<T,bool>> or a Func<T,bool> (or any delegate type for that matter), but in this context it cannot generate more than one at once.

For example, I can write:

public Expression<Func<Pair,bool>> PairMatchesExpression()
{
    return x => x.A == x.B;
}

as easily as I can write:

public Func<Pair,bool> PairMatchesDelegate()
{
    return x => x.A == x.B;
}

The problem is that I cannot use the same exact lambda expression (i.e. x => x.A == x.B) in both ways, without physically duplicating it into two separate methods with two different return types, in spite of the compiler's ability to compile it into either one.

In other words, if I'd like to use the lambda expression in the Queryable methods, then I have to use the Expression method signature. Once I do that however, I cannot use it as a Func as easily as I could have had I just declared the method return type as Func. Instead, I now have to call Compile on the Expression and then worry about caching the results manually like so:

static Func<Pair,bool> _cachedFunc;
public Func<Pair,bool> PairMatchesFunc()
{
    if (_cachedFunc == null)
        _cachedFunc = PairMatchesExpression().Compile();
    return _cachedFunc;
}

Is there a solution to this problem so that I can use the lambda expression in a more general way without it being locked down to a particular type at compile-time?

Triynko
  • 18,766
  • 21
  • 107
  • 173

3 Answers3

1

You could create a wrapper class. Something like this:

public class FuncExtensionWrap<T>
{
    private readonly Expression<Func<T, bool>> exp;
    private readonly Func<T, bool> func;

    public FuncExtensionWrap(Expression<Func<T, bool>> exp)
    {
        this.exp = exp;
        this.func = exp.Compile();
    }

    public Expression<Func<T, bool>> AsExp()
    {
        return this;
    }

    public Func<T, bool> AsFunc()
    {
        return this;
    }

    public static implicit operator Expression<Func<T, bool>>(FuncExtensionWrap<T> w)
    {
        if (w == null)
            return null;
        return w.exp;
    }

    public static implicit operator Func<T, bool>(FuncExtensionWrap<T> w)
    {
        if (w == null)
            return null;
        return w.func;
    }
}

And then it would be used like this:

static readonly FuncExtensionWrap<int> expWrap = new FuncExtensionWrap<int>(i => i == 2);

// As expression
Expression<Func<int, bool>> exp = expWrap;
Console.WriteLine(exp.Compile()(2));

// As expression (another way)
Console.WriteLine(expWrap.AsExp().Compile()(2));

// As function
Func<int, bool> func = expWrap;
Console.WriteLine(func(1));

// As function(another way)
Console.WriteLine(expWrap.AsFunc()(2));
Diego
  • 16,436
  • 26
  • 84
  • 136
  • I like this solution of using a single class that provides members to access both the Expression and Func delegate along with implicit cast operators so you don't even have to use those members. It's also nice that it requires only one class per lambda method signature, rather than one class per unique lambda like mine did. It doesn't introduce new problems like race conditions, and it doesn't require maintaining superfluous variable names -- just the static variable you assign the instance to, which is great. It doesn't introduce duplicate copies of the lambda or any extra overhead per call. – Triynko Nov 02 '15 at 00:38
  • *No extra overhead per call (potentially) as long as you're accessing the exp and func variables directly, as opposed to calling the implicit operators which perform the null-check; not that it would be significant nor that it's an unnecessary check for such a static operator. – Triynko Nov 02 '15 at 00:45
  • I'm glad you liked my solution. However @ErikE's though very similar is better. I didn't know that I was missing the `delegator` constraint. That was my first try, but I couldn't do it. So I'm happy to have lerned something new :) – Diego Nov 02 '15 at 11:39
1

Unfortunately, I can see no way to truly get, at compile time, a Func and an Expression from the same lambda. However, you could at least encapsulate away the difference, and you can also defer the compilation of the Func until the first time it's used. Here's a solution that makes the best of things and may meet your needs, even though it doesn't quite go all the way to what you really wanted (compile-time evaluation of both the Expression and the Func).

Please note that this works fine without using the [DelegateConstraint] attribute (from Fody.ExtraConstraints), but with it, you will get compile-time checking of the constructor parameter. The attributes make the classes act like they have a constraint where T : Delegate, which is not currently supported in C#, even though it is supported in the ILE (not sure if I'm saying that right, but you get the idea).

public class VersatileLambda<[DelegateConstraint] T> where T : class {
    private readonly Expression<T> _expression;
    private readonly Lazy<T> _funcLazy;

    public VersatileLambda(Expression<T> expression) {
        if (expression == null) {
            throw new ArgumentNullException(nameof(expression));
        }
        _expression = expression;
        _funcLazy = new Lazy<T>(expression.Compile);
    }

    public static implicit operator Expression<T>(VersatileLambda<T> lambda) {
        return lambda?._expression;
    }

    public static implicit operator T(VersatileLambda<T> lambda) {
        return lambda?._funcLazy.Value;
    }

    public Expression<T> AsExpression() { return this; }
    public T AsLambda() { return this; }
}

public class WhereConstraint<[DelegateConstraint] T> : VersatileLambda<Func<T, bool>> {
    public WhereConstraint(Expression<Func<T, bool>> lambda)
        : base(lambda) { }
}

The beauty of the implicit conversion is that in contexts where a specific Expression<Func<>> or Func<> is expected, you don't have to do anything at all, just, use it.

Now, given an object:

public partial class MyObject {
    public int Value { get; set; }
}

That is represented in the database like so:

CREATE TABLE dbo.MyObjects (
    Value int NOT NULL CONSTRAINT PK_MyObjects PRIMARY KEY CLUSTERED
);

Then it works like this:

var greaterThan5 = new WhereConstraint<MyObject>(o => o.Value > 5);

// Linq to Objects
List<MyObject> list = GetObjectsList();
var filteredList = list.Where(greaterThan5).ToList(); // no special handling

// Linq to Entities
IQueryable<MyObject> myObjects = new MyObjectsContext().MyObjects;
var filteredList2 = myObjects.Where(greaterThan5).ToList(); // no special handling

If implicit conversion isn't suitable, you can cast explicitly to the target type:

var expression = (Expression<Func<MyObject, bool>>) greaterThan5;

Note that you don't really need the WhereConstraint class, or you could get rid of VersatileLambda by moving its contents to WhereConstraint, but I liked making the two separate (as now you can use VersatileLambda for something that returns other than a bool). (And this difference is largely what sets apart my answer from Diego's.) Using VersatileLambda as it is now looks like this (you can see why I wrapped it):

var vl = new VersatileLambda<Func<MyObject, bool>>(o => o.Value > 5);

I have confirmed that this works perfectly for IEnumerable as well as IQueryable, properly projecting the lambda expression into the SQL, as proven by running SQL Profiler.

Also, you can do some really cool things with expressions that can't be done with lambdas. Check this out:

public static class ExpressionHelper {
    public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
        this Expression<Func<TFrom, TMiddle>> first,
        Expression<Func<TMiddle, TTo>> second
    ) {
        return Expression.Lambda<Func<TFrom, TTo>>(
           new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
           first.Parameters
        );
    }

    // this method thanks to Marc Gravell   
    private class SwapVisitor : ExpressionVisitor {
        private readonly Expression _from;
        private readonly Expression _to;

        public SwapVisitor(Expression from, Expression to) {
            _from = from;
            _to = to;
        }

        public override Expression Visit(Expression node) {
            return node == _from ? _to : base.Visit(node);
        }
    }
}

var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);

You can create an overload of Chain that takes a VersatileLambda as the first parameter, and returns a VersatileLambda. Now you're really sizzling along.

ErikE
  • 48,881
  • 23
  • 151
  • 196
  • Your "VersatileLambda" is exactly what I had originally envisioned as a solution before realizing C# doesn't support a "delegate" as a type constraint. I ran into Fody's workaround and I read the same thing you stated -- that it's a compiler limitation, not a .NET limitation. I would choose Diego's solution over this, only because this introduces a dependency on Fody, but if delegate was natively supported as a type constraint, then this solution is best because it requires only one class, which will support any lambda. I wish Delegate and "Numeric" type constraints were supported. – Triynko Nov 02 '15 at 01:04
  • On second though, I am going to mark this as the answer, because it will work even without Fody. The `Expression` class itself requires a delegate type, yet settles for naming the generic type parameter `TDelegate`; so, if it's good enough for the framework, it's good enough for me. Worst case, if a non-delegate type is used, it will error out in `Expression`'s final cast in its `TDelegate Compile` call: `(TDelegate)((object)LambdaCompiler.Compile(this, null));` – Triynko Nov 02 '15 at 01:29
  • For what it's worth, Diego's and my code are very similar, and he did post first, but it was almost exactly what I was thinking of. I also went to some substantial trouble to validate that the implicit conversion works just fine in a linq to entities statement. I do think an AsFunc or ToFunc method is worthwhile. (And Expression.) I also think the chaining I presented is added value. And I like the name I selected better. :) – ErikE Nov 02 '15 at 02:14
  • VersatileLamda is indeed more descriptive. Personally, I named mine `AutoLambda`, mirroring the C++ `auto` return type keyword that achieves the same purpose. These answers are all similar, but this one is also significantly different from the others in that it explicitly points out the need for a Delegate type constraint and provides a compile-time solution via Fody. It also gives examples of how the class can be used alone or extended to support a specific type of delegate such as the predicate `Func`. – Triynko Nov 02 '15 at 02:39
  • The Chain method is added-value in this context, because this problem is specifically concerned with complex lambdas (i.e. lambdas that we're actually worried about having to repeat or maintain in two different forms), so the Chain method would indeed allow us to break Expressions down in ways that would not be possible with lambas alone. I also agree that having explicit ToFunc/ToExpression methods would be useful. – Triynko Nov 02 '15 at 02:39
  • @Triynko I updated my answer. While the updated class is a little more complicated, I believe it is a functional improvement because it avoids compiling the `Func` until it is used. It also does the caching in a way that I think is better--rather than checking `null` each time, it rewrites the method that's being used. After the first fetch of the `Func`, no further checking is done. I've also followed the double-checking locking pattern, which should be safe (it wasn't in early versions of C#, but I believe it is now). – ErikE Nov 02 '15 at 18:20
  • @Triynko Update: the proper way to do this is with a `Lazy`, as now shown. – ErikE Nov 02 '15 at 21:37
  • I actually prefer the original implementation. The `?.` (null conditional member access or null-propagation operator) is new in Visual Studio 2015 (C# 6.0), and it performs the same null check every time, so it's not really an optmization. The Lazy class introduces a lot of extra logic and multiple internal method calls with volatile reads, locks, and checks of LazyThreadSafetyMode. I'd rather have the initialization take place up-front, rather than causing a stutter on first-runs. – Triynko Nov 09 '15 at 16:27
  • The null conditional member access is just there because I was playing around with VS 2015. It's identical to the null check, as you say, and wasn't intended to be an optimization of speed, just a clarity improver. I understand about your preference to not lazily .Compile the Expression, but see that as an implementation-specific issue, not a timeless, global one. I'll present both versions later. – ErikE Nov 09 '15 at 16:39
-2

Here is one workaround. It generates an explicit class for the expression (as the compiler would do under the hood anyway with lambda expressions that require a function closure) instead of just a method, and it compiles the expression in a static constructor so it doesn't have any race conditions that could result in multiple compilations. This workaround still incurs an additional runtime delay as a result of the Compile call which could otherwise be offloaded to build-time, but at least it's guaranteed to run only once using this pattern.

Given a type to be used in the expression:

public class SomeClass
{
    public int A { get; set; }
    public int? B { get; set; }
}

Build an inner class instead of a method, naming it whatever you would have named the method:

static class SomeClassMeetsConditionName
{
    private static Expression<Func<SomeClass,bool>> _expression;
    private static Func<SomeClass,bool> _delegate;
    static SomeClassMeetsConditionName()
    {
        _expression = x => (x.A > 3 && !x.B.HasValue) || (x.B.HasValue && x.B.Value > 5);
        _delegate = _expression.Compile();
    }
    public static Expression<Func<SomeClass, bool>> Expression { get { return _expression; } }
    public static Func<SomeClass, bool> Delegate { get { return _delegate; } }
}

Then instead of using Where( SomeClassMeetsConditionName() ), you simply pass SomeClassMeetsConditionName followed by either .Delegate or .Expression, depending on the context:

public void Test()
{
    IEnumerable<SomeClass> list = GetList();
    IQueryable<SomeClass> repo = GetQuery();

    var r0 = list.Where( SomeClassMeetsConditionName.Delegate );
    var r1 = repo.Where( SomeClassMeetsConditionName.Expression );
}

As an inner class, it could be given an access level just like a method and accessed just like a method, and even collapsed all at once like a method, so if you can stand to look at the class instead of a method, this is a functional workaround. It could even be made into a code template.

Triynko
  • 18,766
  • 21
  • 107
  • 173
  • He asked for a way to have the lambda compiled into an expression and a delegate without compiling the delegate at runtime; you're compiling it at runtime. – Servy Oct 29 '15 at 00:07
  • I never said 'without compiling the delegate at runtime'. I asked if there was a way to prevent/stop it from being 'locked down to a particular type at compile-time'. You're putting too much emphasis on the 'compile-time' and missing the point that I'm trying to not duplicate a complex expression, whilst having the ability to use it as both an Expression and a Delegate for use with Queryable and Enumerable methods. The "Compile" method undoes the compile-time lock-down as an Expression, so your comment is invalid. – Triynko Oct 29 '15 at 03:31
  • Given that the question specifically mentions the ability to compile expressions, and you went on for quite some time in comments about how that wasn't solving your problem, apparently it didn't solve your problem, so posting it as an answer is pretty nonsensical. Apparently you never had a question in the first place, if the answer is functionally identical to the code in the question. – Servy Oct 29 '15 at 03:40
  • No, it's not functionally equivalent. The workaround in the question was susceptible to a race condition; two threads could hit the method at the same time, both see the variable as null, and both call Compile, resulting in redundant compilation. There's also an "if" statement that runs every time in the question, but not so in my answer. Form is also important. The original workaround clutters the class with static variables that need to be maintained with unique and well-identified names. In my solution, they're encapsulated locally in classes, so "_expression" and "_delegate" suffice. – Triynko Oct 29 '15 at 16:12
  • And I'm still interested in hearing if there are any other workarounds. – Triynko Oct 29 '15 at 16:12
  • So you're saying that the entirety of your question is asking how to remove that one race condition, and that everything about compiling expressions as lambdas or delegates is unrelated to what you're actually asking? If so, that not clear *at all* from your question. – Servy Oct 29 '15 at 16:19
  • I agree with @Servy: Triynko, you should put in order what you need of the solution and what are their priorities. Also, if you are still interested in other workarounds, I believe my answer is a good way to go (at least for some of the things you wanted to avoid). – Diego Oct 29 '15 at 20:21
  • This requires one class per lambda. That is not very great. See [Diego's answer](http://stackoverflow.com/a/33415686/57611). – ErikE Oct 29 '15 at 20:30
  • @Servy No, the question is simple. Lambas compile to either expression or delegate, which results in extra work being required to use it in both the Enumerble and Queryable APIs (e.g. duplication of the lamba itself, runtime compilation of the Expression, etc.), which would not be necessary if you were only using one or the other. The problem (i.e. the question) is to find a solution that minimizes that otherwise unnecessary effort, without introducing new problems (e.g. race condition, additional computations like null-checks with every call, maintaining new variable names, etc.). – Triynko Nov 02 '15 at 00:21
  • @Diego I like your solution of using a single class that provides members to access both the Expression and Func delegate along with implicit cast operators so you don't even have to use the members. It's also nice that it requires only one class per lambda method signature, rather than one class per unique lambda like mine. It doesn't introduce race conditions, it doesn't require any special variable names, except for the static variable you assign the instance to, which is great. It doesn't require maintaining duplicate copies of the lambda or introduce unnecessary extra overhead per call. – Triynko Nov 02 '15 at 00:30