Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Expression<Func<>> in a LINQ Query

I want to define a Func<ProductItemVendor, bool> filter expression named CompareProductItemVendorIds, which can be used throughout my application, mostly within Entity Framework/LINQ queries.

I've learned that in order to be able to use this filter in a LINQ query, I must declare it as Expression<Func<>> instead of just Func<>. I understand the reason for this, and it's easy for me to do this.

But I'm having the following problems using that expression in my queries.

First, code such as:

ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds)

Note: ProductItem is a database entity, and its ProductItemVendors property is a navigation collection.

Produces the error:

`Instance argument: cannot convert from 'System.Collections.Generic.ICollection' to 'System.Linq.IQueryable'

And second, code such as:

var results = from v in Repository.Query<ProductItemVendor>()
              where CompareProductItemVendorIds(v)
              select v;

Produces the error:

'CompareProductItemVendorIds' is a 'variable' but is used like a 'method'

So I have my nice shiny new Expression<Func<>>. How can I use it in my LINQ queries?

like image 919
Jonathan Wood Avatar asked Oct 28 '14 20:10

Jonathan Wood


2 Answers

ProductItem is already an Entity, so you can't use your Expression, you need to use Compile() to get the Func<> from your Expression<Func<>> since ProductItemVendors is no longer an IQueryable

ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds.Compile())

You would have to use your Expression on the ProductItemVendorsContext like this:

var item = Context.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds);

You cant use an Expression inside query syntax, you need to use method sytanx

var results = from v in Repository.Query<ProductItemVendor>()
                                  .Where(CompareProductItemVendorIds)
              select v;
like image 71
Aducci Avatar answered Oct 04 '22 20:10

Aducci


I don't think composing expressions like that is supported by default.
You could try LINQKit

Here's an example from the LINQKit page; one Expression<Func<>> inside another:

Call Invoke to call the inner expression Call Expand on the final result. For example:

Expression<Func<Purchase,bool>> criteria1 = p => p.Price > 1000;
Expression<Func<Purchase,bool>> criteria2 = p => criteria1.Invoke (p)
                                                 || p.Description.Contains ("a");

Console.WriteLine (criteria2.Expand().ToString()); 

(Invoke and Expand are extension methods in LINQKit.) Here's the output:

p => ((p.Price > 1000) || p.Description.Contains("a"))

Notice that we have a nice clean expression: the call to Invoke has been stripped away.

like image 30
user5233494 Avatar answered Oct 04 '22 21:10

user5233494