hyankov
hyankov

Reputation: 4129

How can I pass an entity property as a parameter of a LINQ Expression?

How can I pass an entity property as a parameter of a LINQ Expression?

public DropdownFilter(WhatTypeHere? ABC)
{
    // I want to store a selected property here
    // ABC must be a property of TEntity
    this.ABC = ABC;
}

// I want the class to encapsulate a LINQ query and just parametrize it with a property
public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
    // ABC is a property of TEntity
    return filteredEntityCollection.Where(this.ABC == value);
}

I will be using it like this:

new DropdownFilter<Invoice>(invoice => invoice.SomeProperty);

I have already tried with Expression<Func<TEntity, string>> kind of parameter, but it didn't work out. It's complaining about

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

Upvotes: 1

Views: 1998

Answers (1)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236248

You have to build LINQ expression manually. First of all I would add second generic parameter to your class, which would specify type of property and type of value which you would pass to filter:

public class DropdownFilter<TEntity, TProperty>

Next you should pass property selector expression to constructor of this filter class:

private PropertyInfo propertyInfo;

public DropdownFilter(Expression<Func<TEntity, TProperty>> propertySelector)
{
    this.propertyInfo = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;
}

And last, build lambda expression to filter queryable by given value of specified property:

public IQueryable<TEntity> Filter(
   IQueryable<TEntity> filteredEntityCollection, TProperty value)
{
    var param = Expression.Parameter(typeof(TEntity), "p");

    var lambda = Expression.Lambda<Func<TEntity, bool>>(                
        Expression.Equal(
            Expression.Property(param, propertyInfo),
            Expression.Constant(value)
        ), param);

    return filteredEntityCollection.Where(lambda);
}

Usage:

var filter = new DropdownFilter<Invoice, string>(i => i.ABC);
var result = filter(db.Invoices, "foo"); // strongly-typed parameter here

I would add validation of property selector expression passed to the constructor. You should check whether it's a MemberExpression. And you can verfiy property type to support only primitive types of properties.

Upvotes: 3

Related Questions