Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the purpose of adding compiled Func methods together?

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!

like image 297
Ian Robertson Avatar asked Mar 30 '13 18:03

Ian Robertson


People also ask

Why do we need extension methods in C#?

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type.

What is compiling and linking in C++?

Each C++ source file needs to be compiled into an object file. The object files resulting from the compilation of multiple source files are then linked into an executable, a shared library, or a static library (the last of these being just an archive of object files).

What is compiling in C++?

Compiling and Linking in C++ The compilation is the process which convert the program written in human readable language like C, C++ etc into a machine code, directly understood by the Central Processing Unit. There are many stages involved in creating a executable file from the source file.

What is the difference between method and function in C#?

Methods and Functions are the same in C#. However, Methods are used in C# and are functions that operate through a designated class. A method is a group of statements that together perform a task. Every C# program has at least one class with a method named Main.


3 Answers

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.

like image 192
Lasse V. Karlsen Avatar answered Sep 28 '22 15:09

Lasse V. Karlsen


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

like image 30
Nick Butler Avatar answered Sep 28 '22 16:09

Nick Butler


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.

like image 43
Gert Arnold Avatar answered Sep 28 '22 16:09

Gert Arnold