Question

I have seen that is is possible to add compiled methods together.

Expression<Func<Customer, bool>> ln = c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase);
Expression<Func<Customer, bool>> fn = c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase);
Expression<Func<Customer, bool>> fdob = c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd"));

var filter = ln.Compile() + fn.Compile() + fdob.Compile();

Does it make sense to do this?

I would intend to use the filter in place of a lambda expression to filter a repository of customers:

IEnumerable<Customer> customersFound = _repo.Customers.Where(filter);

Depending on business logic, I may or may not add the three compiled methods together, but pick and choose, and possibly add more compiled methods as I go.

Can anyone explain if adding them together would build up a query string or not? Anyone got a better suggestion?

I could chain "Where" statements and use regular lambda expressions, but I am intrigued by what you might gain from compiling methods and adding them up!

Was it helpful?

Solution 3

It is a creative effort to chain predicates and too bad it does not work. The reason why has been explained excellently by Lasse and Nicholas (+2). But you also ask:

Anyone got a better suggestion?

The drawback of compiled expressions is that they're not expressions any more and thus, can't be used with IQueryables that are backed by SQL query providers (like linq to sql or linq to entities). I assume _repo.Customers is such an IQueryable.

If you want to chain expressions dynamically, LINQKit's PredicateBuilder is an excellent tool to do this.

var pred = Predicate.True<Customer>();

pred = pred.And(c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase);
pred = pred.And(c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase);
pred = pred.And(c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd"));

var customersFound = _repo.Customers.Where(pred.Expand());

This is what the Expand is for:

Entity Framework's query processing pipeline cannot handle invocation expressions, which is why you need to call AsExpandable on the first object in the query. By calling AsExpandable, you activate LINQKit's expression visitor class which substitutes invocation expressions with simpler constructs that Entity Framework can understand.

Or: without it an expression is Invoked, which causes an exception in EF:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

BTW. If you use linq to sql/entities you may as well use c.lastname == _customer.lastName because it is translated into SQL and the database collation will determine case sensitiveness.

OTHER TIPS

That's a side-effect of the fact that the Compile method returns a delegate.

Internally, a delegate is a multicast-delegate, it can involve multiple chained references to methods.

The + is in this case just a quick way to chain them together. You can read more about combining delegates in the How to: Combine Delegates on MSDN.

Here's a LINQPad program that demonstrates:

void Main()
{
    var filter = GetX() + GetY() + GetZ();
    filter().Dump();
}

public int X() { Debug.WriteLine("X()"); return 1; }
public int Y() { Debug.WriteLine("Y()"); return 2; }
public int Z() { Debug.WriteLine("Z()"); return 3; }

public Func<int> GetX() { return X; }
public Func<int> GetY() { return Y; }
public Func<int> GetZ() { return Z; }

Output:

X()
Y()
Z()
3

Note that it thus seems to just disregard the return value from the first method calls, and returns only the last, although it fully calls the prior methods as well.

Note that the above code is similar to this:

void Main()
{
    Func<int> x = X;
    Func<int> y = Y;
    Func<int> z = Z;

    var filter = x + y + z;
    filter().Dump();
}

public int X() { Debug.WriteLine("X()"); return 1; }
public int Y() { Debug.WriteLine("Y()"); return 2; }
public int Z() { Debug.WriteLine("Z()"); return 3; }

The short answer is thus that no, this does not do what you want it to do.

When you call Compile(), you are creating instances of type Func<Customer, bool>, which is a delegate.

Delegates overload the operator+ to return Multicast Delegates, which is what is happening here.

See MSDN: How to: Combine Delegates (Multicast Delegates)

So, no - adding them together would not build up a query string.


If this is for EF, you want to use Expression Trees, not Lambdas.

You can create and modify Expression Trees using the System.Linq.Expressions namespace:

MSDN: System.Linq.Expressions Namespace

and

MSDN: How to: Use Expression Trees to Build Dynamic Queries

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top