Reputation: 656
I have built some extension method for sorting and filtering generic objects using an IQueryable to get the data from, so far it works great for all types, but for enums, and i cant figure how to get around this, so here is some example.
public enum ClaimStatusEnum
{
None = 0,
Open = 10,
Submitted = 20,
Rejected = 30,
Refunded = 40,
Closed = 50,
Received = 60,
Backorder = 70,
UnderReview = 80
}
please note! this is just an example of an enum, but i dont know at the time of execution what the enum type will be, so be aware that i must use generic type.
now here is an example that holds a property with that enum type in it.
public class Claim
{
[Key]
public int Id { get; set; }
public DateTime DateCreated { get; set; }
public ClaimStatusEnum ClaimStatus { get; set; }
}
now here is an example of a filter that is being passed with a list of int values from the enum.
var arr = new int[] { 20, 30, 40 };
now the goel in this example is to find in the Claim table where we have records with the enum value that we have in the arr of int.
so i want to build a lambda expression, and it should be generic and not hard coding the types of the classes, given it could be any entity and also should be working for any given enum.
so here is what i currently use for the exact same thing, but with the only difference that its for a property that is an int not an enum, so it works great for int types, but when it comes to enum types i get an error.
ConstantExpression c = Expression.Constant(arr);
MethodInfo mi = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Contains")
.Single(m => m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
call = Expression.Call(mi, c, m);
can anyone help me here how i can make this work for enum types? i have tried already so many different ways, they all throw an error.
here is how this is being used as an extension method.
public static IQueryable<T> EntitySortAndFilter<T>(this IQueryable<T> data, PageFilter filter, T type)
{
foreach (var item in filter.Filter.Filters)
{
if (item.Value == null || item.Condition == null || item.Field == null)
continue;
ParameterExpression e;
Expression m, call;
PropertyInfo prop;
prop = type.GetType().GetProperty(item.Field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
e = Expression.Parameter(type.GetType(), "e");
m = Expression.MakeMemberAccess(e, prop );
var arr = item.Value;
ConstantExpression c = Expression.Constant(arr);
MethodInfo mi = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Contains")
.Single(m => m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
call = Expression.Call(mi, c, m);
var lambda = Expression.Lambda<Func<T, bool>>(call, e);
data = data.Where(lambda);
}
return data;
}
and when calling this extension, i call it this way.
var example = query.EntitySortAndFilter(filter, new Claim());
but again, it should be working for any entity that i want, thats why did it generic.
Upvotes: 1
Views: 1294
Reputation: 141865
I think you need to do something like this:
if(prop.PropertyType.IsEnum) // if property is enum
{
// create expression to cast array values to enum
var castMi = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(prop.PropertyType);
var castArrToEnum = Expression.Call(castMi, Expression.Constant(arr)));
// make contains for needed type
mi = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Contains")
.Single(m => m.GetParameters().Length == 2)
.MakeGenericMethod(prop.PropertyType);
call = Expression.Call(mi, castArrToEnum, m);
}
If this does not work you can always try to go other way around:
var mc = Expression.Convert(m, typeof(int));
ConstantExpression c = Expression.Constant(arr);
MethodInfo mi = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Contains")
.Single(m => m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
call = Expression.Call(mi, c, mc);
Upvotes: 1