Dawood Awan
Dawood Awan

Reputation: 7338

Trying to write a Generic Filter IQueryable

I am trying to write a generic filter. This filter is being used for nearly every table in my DB. I need a generic Equals filter so I can apply to all my tables without repeating my code everywhere

: I have the following code:

public static IQueryable<T> ApplyFilterModel<T>(this IQueryable<T> source, FilterDTO filterModel)
{
    var type = typeof(T);

    if(filterModel.SelectedCompanyId != 0)
    {
        var property = type.GetProperty("iCompanyId");
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExp = Expression.Lambda(propertyAccess, parameter);
        var resultExp = Expression.Call(typeof(Queryable), "Equals", new[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
        source = source.Provider.CreateQuery<T>(resultExp);
    }

    return source;
}

type.GetProperty("iCompanyId"); The iCompanyId will always be an Int32.

So when I am debugging and reach this line:

var resultExp = Expression.Call(typeof(Queryable), "Equals", new[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));

I get this Error:

enter image description here

What am I doing wrong here?

I need to filter the Input IQueryable if iCompanyId == filterModel.SelectedCompanyId

This is how I am calling the filter:

telematicDevices.ApplyFilterModel(model.FilterDTO);

Where telematicDevices is an IQueryable from the DB (Entity Framework)

Upvotes: 0

Views: 1340

Answers (3)

AIDA
AIDA

Reputation: 529

Can you not apply a .Where statement?

//Interface we need to access property
public interface ICompanySpecific
{
    public int iCompanyId { get; }
}

For each object that has iCompanyId create a partial class to specifiy the interface.

public partial class Person : ICompanySpecific
{
    //No code is needed because Person already has the iCompanyId property
}

And now you can use this with Person or any type that implements ICompanySpecific

public static IQueryable<T> ApplyFilterModel<T>(this IQueryable<T> source, FilterDTO filterModel)
    : where T: ICompanySpecific
{
    return source.Where(o => o.iCompanyId == filterModel.SelectedCompanyId);
}

Upvotes: 3

Servy
Servy

Reputation: 203844

The expression that you're creating, if written out statically, would look something like this:

public static IQueryable<T> ApplyFilterModel<T>(this IQueryable<T> source,
    FilterDTO filterModel)
{
    return Queryable.Equals<T, int>(source, p => p.iCompanyId);
}

What you actually want to do is create the dynamic version of this:

public static IQueryable<T> ApplyFilterModel<T>(this IQueryable<T> source,
    FilterDTO filterModel)
{
    return Queryable.Where<T, int>(source, 
        p => p.iCompanyId == filterModel.SelectedCompanyId);
}
  1. You need to have your lambda actually compare the property that you have to filterModel.SelectedCompanyId. This would mean using Expression.Equals passing in both the projected property and a constant representing the value to compare it with.
  2. Use Where instead of Equals for the Expression.Call call. (Or, for that matter, just calling Where as a method, instead of building an expression representing that call, because it will represent itself as an expression through its implementation.)

And that's assuming you don't want to make your life easy and just write out the implementation statically which is not only much easier, but also allows for static typing.

Upvotes: 0

Nick Cipollina
Nick Cipollina

Reputation: 551

You could always try something like this:

public static IQueryable<T> ApplyFilterModel<T>(this IQueryable<T> source, FilterDTO filterModel)
{
var type = typeof(T);

if(filterModel.SelectedCompanyId != 0)
{
    var parameterExp = Expression.Parameter(type, "type");
    var propertyExp = Expression.Property(parameterExp, "iCompanyId");
    var constExp = Expression.Constant(filterModel.SelectedCompanyId, typeof(int)); // I'm assuming that SelectedCompanyId is an int.
    var equalExp = Expression.Equal(propertyExp, constExp);
    var lambda = Expression.Lambda<Func<T, bool>>(equalExp, constExp);
    source = source.Provider.Where(lambda);
}

return source;

}

Upvotes: 1

Related Questions