【问题标题】:OrderBy Expression Tree in Net Core Linq for Extension MethodNet Core Linq 中用于扩展方法的 OrderBy 表达式树
【发布时间】:2019-12-26 08:13:55
【问题描述】:

我想创建一个模仿这个的扩展方法,https://dejanstojanovic.net/aspnet/2019/january/filtering-and-paging-in-aspnet-core-web-api/

但是,我想在 StartsWith 之后添加一个 OrderBy(对于 ColumnName),我将如何执行此操作?

尝试添加以下但没有工作.OrderBy(参数)

示例:

return persons.Where(p => p.Name.StartsWith(filterModel.Term ?? String.Empty, StringComparison.InvariantCultureIgnoreCase))
   .OrderBy(c=>c.Name)  
   .Skip((filterModel.Page-1) * filter.Limit)  
   .Take(filterModel.Limit);  


public static class PaginateClass
{
    static readonly MethodInfo startsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string), typeof(System.StringComparison) });

    public static IEnumerable<T> Paginate<T>(this IEnumerable<T> input, PageModel pageModel, string columnName) where T : class
    {
        var type = typeof(T);
        var propertyInfo = type.GetProperty(columnName);
        //T p =>
        var parameter = Expression.Parameter(type, "p");
        //T p => p.ColumnName
        var name = Expression.Property(parameter, propertyInfo);
        // filterModel.Term ?? String.Empty
        var term = Expression.Constant(pageModel.Term ?? String.Empty);
        //StringComparison.InvariantCultureIgnoreCase
        var comparison = Expression.Constant(StringComparison.InvariantCultureIgnoreCase);
        //T p => p.ColumnName.StartsWith(filterModel.Term ?? String.Empty, StringComparison.InvariantCultureIgnoreCase)
        var methodCall = Expression.Call(name, startsWith, term, comparison);

        var lambda = Expression.Lambda<Func<T, bool>>(methodCall, parameter);


            return input.Where(lambda.Compile()) //tried adding this and did not work .OrderBy(parameter)  
            .Skip((pageModel.Page - 1) * pageModel.Limit)
            .Take(pageModel.Limit);

    }

其他项目PageModel:

public class PageModel
{

    public int Page { get; set; }
    public int Limit { get; set; }
    public string Term { get; set; }

    public PageModel()
    {
        this.Page = 1;
        this.Limit = 3;
    }

    public object Clone()
    {
        var jsonString = JsonConvert.SerializeObject(this);
        return JsonConvert.DeserializeObject(jsonString, this.GetType());
    }
}

Dynamic Linq to Entities Orderby with Pagination

【问题讨论】:

  • 您希望OrderBy 提供动态列名,创建一个与StartsWith 相同的表达式,它将是MethodCallExpression 或检查System.Linq.Dynamic,它们提供开箱即用的扩展方法
  • 嗨 @MrinalKamboj 公司不允许使用 linqdymiac,但是,请随时在回答中给出第一个建议,我可以发送积分,谢谢!
  • 这是一个非常好的开源库,被你们屏蔽了
  • 好的,我去看看,是这个吗? var test = MethodCallExpression.Call(typeof(Queryable), "OrderBy", new[] { type, propertyInfo.PropertyType }); return input.Where(lambda.Compile()).test .Skip((pageModel.Page - 1) * pageModel.Limit) .Take(pageModel.Limit);

标签: c# .net linq .net-core expression-trees


【解决方案1】:

查看示例代码以获取解决方案:

void Main()
{
    var queryableRecords = Product.FetchQueryableProducts();

    Expression expression = queryableRecords.OrderBy("Name");

    var func = Expression.Lambda<Func<IQueryable<Product>>>(expression)
                         .Compile();

    func().Dump();
}

public class Product
{
    public int Id { get; set; }

    public string Name { get; set; }

    public static IQueryable<Product> FetchQueryableProducts()
    {
        List<Product> productList = new List<Product>()
        {
          new Product {Id=1, Name = "A"},
          new Product {Id=1, Name = "B"},
          new Product {Id=1, Name = "A"},
          new Product {Id=2, Name = "C"},
          new Product {Id=2, Name = "B"},
          new Product {Id=2, Name = "C"},
        };

        return productList.AsQueryable();
    }
}

public static class ExpressionTreesExtesion
{

    public static Expression OrderBy(this IQueryable queryable, string propertyName)
    {
        var propInfo = queryable.ElementType.GetProperty(propertyName);

        var collectionType = queryable.ElementType;

        var parameterExpression = Expression.Parameter(collectionType, "g");
        var propertyAccess = Expression.MakeMemberAccess(parameterExpression, propInfo);
        var orderLambda = Expression.Lambda(propertyAccess, parameterExpression);
        return Expression.Call(typeof(Queryable),
                               "OrderBy",
                               new Type[] { collectionType, propInfo.PropertyType },
                               queryable.Expression,
                               Expression.Quote(orderLambda));

    }


}

结果

工作原理:

Queryable 类型上使用扩展方法创建了一个表达式,它在内部调用Queryable 类型的OrderBy 方法,期望IQueryable 作为输入,以及字段名称,从而运行排序函数而有序集合是最终的输出

选项 2:

这可能更适合您的用例,这里不是调用OrderBy 方法,而是创建Expression&lt;Func&lt;T,string&gt;&gt; 作为IEnumerable&lt;T&gt; 的扩展方法,然后可以将其编译并提供给OrderBy Call,如显示在示例中,因此是更直观和简单的解决方案:

创建表达式:

public static class ExpressionTreesExtesion
{
    public static Expression<Func<T,string>> OrderByExpression<T>(this IEnumerable<T> enumerable, string propertyName)
    {
        var propInfo = typeof(T).GetProperty(propertyName);

        var collectionType = typeof(T);

        var parameterExpression = Expression.Parameter(collectionType, "x");
        var propertyAccess = Expression.MakeMemberAccess(parameterExpression, propInfo);
        var orderExpression = Expression.Lambda<Func<T,string>>(propertyAccess, parameterExpression);
        return orderExpression;
    }
}

如何拨打电话:

var ProductExpression = records.OrderByExpression("Name");

var result  = records.OrderBy(ProductExpression.Compile());

上面的ProductExpression.Compile() 将编译成x =&gt; x.Name,其中列名是在运行时提供的

请注意,如果排序字段可以是字符串数据类型之外的其他类型,那么将其设为通用并在调用扩展方法时提供它,只有被调用的属性的条件必须与提供的值具有相同的类型,否则它将是一个运行时异常,同时创建表达式

编辑1,如何让OrderType字段也通用

public static Expression<Func<T, TField>> OrderByFunc<T,TField>(this IEnumerable<T> enumerable, string propertyName)
    {
        var propInfo = typeof(T).GetProperty(propertyName);

        var collectionType = typeof(T);

        var parameterExpression = Expression.Parameter(collectionType, "x");
        var propertyAccess = Expression.MakeMemberAccess(parameterExpression, propInfo);
        var orderExpression = Expression.Lambda<Func<T, TField>>(propertyAccess, parameterExpression);
        return orderExpression;
    }

如何打电话:

现在这两种类型都需要显式提供,之前使用来自IEnumerable&lt;T&gt; 的泛型类型推断:

// Integer Id 字段

var ProductExpression = records.OrderByFunc&lt;Product,int&gt;("Id");

//对于字符串名称字段

var ProductExpression = records.OrderByFunc&lt;Product,string&gt;("Name");

【讨论】:

  • 好的,我们实际上正在使用 IEnumerable,但我会检查一下,IEnumerable 不包含 ElementType 的定义,谢谢!
  • 勾选选项2,应该可以,理想情况下你应该使用IEnumerable&lt;T&gt;,然后你不需要元素类型,因为我们可以直接访问类型。另请注意IQueryable is a type of IEnumerable`,这是主要的基本接口
  • 嗨 Mrinal,我很抱歉,我如何按变量排序,以便它接受整数、字符串和其他数据类型?我应该在方法中将所有内容都转换为字符串,还是有更好的概括方法?如果我应该提出另一个问题,请告诉我,我的同事也对这个问题竖起了大拇指:)
  • @MattSmith 检查Edit 1, how to make the OrderType field also generic 更新,如果您有任何其他问题,请告诉我
猜你喜欢
  • 2016-04-17
  • 1970-01-01
  • 2020-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
  • 1970-01-01
相关资源
最近更新 更多