Search code examples
c#ef-core-2.0linqkitef-core-2.1

Exception trying to use a basic Expression created with LinqKit


I'm trying to force ef-core to paramerize values that it considers constants as described in this article by using LinqKit.

When trying to apply the PredicateBuilder example described on github, I get an exception. This is my code:

    private async Task getWallets()
    {
        var query =
            from w in _context.Wallets.AsExpandable()
            where w.WalletItems.Any(hasItems().Compile())
            select w;

        var result = await query.ToListAsync();
    }

    private Expression<Func<WalletItem, bool>> hasItems()
    {
        var predicate = PredicateBuilder.New<WalletItem>();
        var ids = new string[] { "A", "B" };

        foreach (var id in ids)
        {
            predicate = predicate.Or(wi => wi.ExternalId == id);
        }
        return predicate;            
    }

When hitting ToListAsync() the following exception:

System.NotSupportedException: 'Could not parse expression 'w.WalletItems.Any(__Compile_0)': The given arguments did not match the expected arguments: Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.'

I am running the code in an ASP.NET Core 2.1 application (netcoreapp2.1) using the following packages:

  • Microsoft.EntityFrameworkCore.Relational (2.1.1)
  • Microsoft.EntityFrameworkCore.SqlServer (2.1.1)
  • LinqKit.Microsoft.EntityFrameworkCore (1.1.15)

Any help much appreciated.


Solution

  • What you are missing in the examples is the fact that all Expression<Func<..>> are either parameters or local variables. While the line

    where w.WalletItems.Any(hasItems().Compile())
    

    is not actually calling the hasItems method, but emits a MethodCallExpression (i.e. Expression.Call) to it inside the query expression tree, which is different type of expression not recognized by LINQKit Expand implementation, thus leading to unsupported expression by EF Core.

    The solution is to put the method call result into a local variable and use that variable inside the query expression tree:

    var hasItemsExpr = hasItems();
    
    var query =
        from w in _context.Wallets.AsExpandable()
        where w.WalletItems.Any(hasItemsExpr.Compile())
        select w;