user2457382
user2457382

Reputation: 349

Pass a parameter to the reusable Expression in a Entity Framework

I want to declare and reuse Expression with filter by variable y. In a method I have something like following:

Expression<Func<Item, int, bool>> exFilter = (x, y) => x.Item.Id == y;

Further on, in a code I'm trying to use declared expression (exFilter)

return context.Item.Select(x => new { data = exFilter.Where(exFilter))

Q: How do I pass parameter to the exFilter? I want to do select filtered by every item in a list(x).

This is just a sample what I'm trying to figure out. The problem and query is much bigger and complicated.

Upvotes: 3

Views: 2307

Answers (3)

Kate
Kate

Reputation: 775

You can rewrite you code like this:

Expression<Func<Item, bool>> exFilter(int y){ return (x) => x.item.Id == y;}

And use it like this:

int paramY = 456;
return context.Item.Select(exFilter(paramY))

Upvotes: 1

Eugene Podskal
Eugene Podskal

Reputation: 10401

You can try something like this:

public class Item
{
    public Item(String str, Int32 @int)
    {
        this.StrValue = str;
        this.IntValue = @int;
    }
    public String StrValue { get; }
    public Int32 IntValue { get; }
    public override string ToString() => 
        $"{this.IntValue} = '{this.StrValue}'";
}


public static class ExpressionExtensions
{
    public static Expression<Func<TItem, TResult>> Curry<TItem, TCurry, TResult>(
        this Expression<Func<TItem, TCurry, TResult>> function,
        TCurry value)
    {
        if (function == null)
            throw new ArgumentNullException(paramName: nameof(function));

        var itemParameter = Expression.Parameter(typeof(TItem));
        var valueConstant = Expression.Constant(value);

        return Expression.Lambda<Func<TItem, TResult>>(
            Expression.Invoke(
                function,
                new Expression[]
                {
                    itemParameter,
                    valueConstant
                }),
            new[] { itemParameter });
    }
}

...

var items = new[]
{
    new Item("one", 1),
    new Item("two", 2),
    new Item("two again", 2),
};

Expression<Func<Item, Int32, Boolean>> predicate = (item, intValue) =>
    item.IntValue == intValue;

var curriedPredicate = predicate.Curry(2);

var filtered = items
    .AsQueryable<Item>()
    .Where(curriedPredicate)
    .ToArray();

foreach (var item in filtered)
{
    Console.WriteLine(item);
}

Upvotes: 0

Yacoub Massad
Yacoub Massad

Reputation: 27871

You can use LinqKit to reuse the expression that you have. Here is an example:

var result =
    context.Item //The DbSet
    .AsExpandable() //This method is defined in LinqKit and allows for expression expansion
    .Where(x => exFilter.Invoke(x, 2)) //LinqKit will know how to translate this into an expression
    .ToList();

I am using the value 2 here as an example.

Upvotes: 4

Related Questions