【问题标题】:Build LINQ Lambda Expression with contains method for enum property with list of int使用包含 int 列表的枚举属性的包含方法构建 LINQ Lambda 表达式
【发布时间】:2020-10-22 20:46:52
【问题描述】:

我已经构建了一些扩展方法,用于使用 IQueryable 对通用对象进行排序和过滤以从中获取数据,到目前为止,它适用于所有类型,但适用于枚举,我不知道如何解决这个问题,所以这里是举个例子。

    public enum ClaimStatusEnum
{
    None = 0,
    Open = 10,
    Submitted = 20,
    Rejected = 30,
    Refunded = 40,
    Closed = 50,
    Received = 60,
    Backorder = 70,
    UnderReview = 80
}

请注意!这只是枚举的一个示例,但我在执行时不知道枚举类型是什么,所以请注意我必须使用泛型类型。

现在这是一个示例,其中包含具有该枚举类型的属性。

 public class Claim
{
    [Key]
    public int Id { get; set; }
    public DateTime DateCreated { get; set; }      
    public ClaimStatusEnum ClaimStatus { get; set; }

}

现在这里是一个过滤器示例,它使用枚举中的 int 值列表传递。

var arr = new int[] { 20, 30, 40 };

现在这个例子中的关键是在 Claim 表中找到我们在 int 的 arr 中具有枚举值的记录。

所以我想构建一个 lambda 表达式,它应该是通用的,而不是硬编码类的类型,因为它可以是任何实体,也应该适用于任何给定的枚举。

所以这是我目前用于完全相同的东西,但唯一的区别是它用于一个 int 不是 enum 的属性,所以它适用于 int 类型,但是当涉及到 enum 类型时,我得到一个错误。

                    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);

任何人都可以在这里帮助我如何使这项工作适用于枚举类型?我已经尝试了很多不同的方法,它们都抛出错误。

这是如何将其用作扩展方法的。

        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;  

    }

当调用这个扩展时,我这样称呼它。

var example = query.EntitySortAndFilter(filter, new Claim());

但同样,它应该适用于我想要的任何实体,这就是为什么它是通用的。

【问题讨论】:

  • 1) 你遇到了什么错误? 2) .MakeGenericMethod(typeof(int)) -> .MakeGenericMethod(typeof(ClaimStatusEnum)) ?
  • 我不能做 typeof(ClaimStatusEnum) 因为它可以是任何给定的枚举,它必须是通用的
  • 您能添加预期的用法吗?您也可以随时为您的枚举类型添加泛型类型参数。
  • 我用示例更新了问题。

标签: c# linq lambda enums expression


【解决方案1】:

我认为你需要这样做:

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);
}

如果这不起作用,您可以随时尝试其他方式:

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);

【讨论】:

  • System.InvalidOperationException: 'NavigationExpandingExpressionVisitor' 处理 LINQ 表达式 'AsQueryable((IEnumerable)int[] { 0, 20, })' 失败。这可能表示 EF Core 中的错误或限制。有关更多详细信息,请参阅go.microsoft.com/fwlink/?linkid=2101433。在 Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
  • 另外,sn-p 有一些错别字,这是我使用的。 call = Expression.Call(mi, castArrToEnum, m); Expression.Constant(arr) 处还有一个额外的 )
  • @SolStein 更改了 call = 部分。你的异常很奇怪,因为我没有将arr 转换为IEnumerable&lt;ClaimStatusEnum&gt; 的地方,我的代码应该生成arr.Cast&lt;ClaimStatusEnum&gt;()
  • 我认为这条线是这样做的 var castMi = typeof(Enumerable).GetMethod("Cast")
  • @SolStein 您也可以随时尝试另一种方式 - 将属性转换为 int - Expression.Convert(m, typeof(int)) 并将其而不是 m 传递给 call
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-01
  • 2020-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多