Reputation: 7338
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:
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
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
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);
}
filterModel.SelectedCompanyId
. This would mean using Expression.Equals
passing in both the projected property and a constant representing the value to compare it with.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
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