1

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.

4
  • 1
    1) What error are you getting? 2) .MakeGenericMethod(typeof(int)) -> .MakeGenericMethod(typeof(ClaimStatusEnum)) ? Commented Oct 22, 2020 at 20:48
  • i cant do typeof(ClaimStatusEnum) because it could be any given enum, it must be generic Commented Oct 22, 2020 at 20:49
  • 1
    Can you please add expected usage? Also you can always add generic type parameter for your enum type. Commented Oct 22, 2020 at 20:51
  • i updated the question with the example. Commented Oct 22, 2020 at 21:04

1 Answer 1

1

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);
Sign up to request clarification or add additional context in comments.

8 Comments

System.InvalidOperationException: Processing of the LINQ expression 'AsQueryable<ClaimStatusEnum>((IEnumerable<ClaimStatusEnum>)int[] { 0, 20, })' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See go.microsoft.com/fwlink/?linkid=2101433 for more detailed information. at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
also, the snippet had some typos, here is what i used. call = Expression.Call(mi, castArrToEnum, m); also there is an extra ) at Expression.Constant(arr)
@SolStein changed the call = part. You exception is very strange cause there is no place where I cast arr to IEnumerable<ClaimStatusEnum>, my code should generate arr.Cast<ClaimStatusEnum>()
i think this line is doing it var castMi = typeof(Enumerable).GetMethod("Cast")
@SolStein also you can always try going another way - casting property to int - Expression.Convert(m, typeof(int)) and passing it instead of m to the call.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.