简体   繁体   English

Net Core Linq中用于扩展方法的OrderBy表达式树

[英]OrderBy Expression Tree in Net Core Linq for Extension Method

I want to create an Extension method which mimics this, https://dejanstojanovic.net/aspnet/2019/january/filtering-and-paging-in-aspnet-core-web-api/ 我想创建一个模仿此的扩展方法https://dejanstojanovic.net/aspnet/2019/january/filtering-and-paging-in-aspnet-core-web-api/

However, I want to add an OrderBy (for ColumnName) after StartsWith, how would I conduct this? 但是,我想在StartsWith之后添加一个OrderBy(用于ColumnName),该如何进行?

tried adding following and did not work .OrderBy(parameter) 尝试添加以下内容,但不起作用.OrderBy(parameter)

Example: 例:

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

    }

Other items PageModel: 其他项目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 带分页的动态Linq到实体Orderby

Check the sample code for the solution: 检查解决方案的样本代码

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

    }


}

Result 结果

在此处输入图片说明

How it Works: 这个怎么运作:

Created an expression using extension method on the Queryable type, which internally calls OrderBy method of the Queryable type, expecting IQueryable to be the Input, along with the field name and thus runs the ordering function and Ordered collection is the final Output 使用创建的扩展方法的一个表达式Queryable类型,其内部调用OrderBy所述的方法Queryable类型,期望IQueryable为输入,与场名称一起并且因此运行排序函数和有序集合是最后的输出

Option 2: 选项2:

This may fit your use case better, here instead of calling OrderBy method, we are creating the Expression<Func<T,string>> as an extension method to the IEnumerable<T> , which can then be compiled and supplied to the OrderBy Call, as shown in the example and is thus much more intuitive and simple solution: 这可能更适合您的用例,这里我们创建Expression<Func<T,string>>作为IEnumerable<T>的扩展方法,而不是调用OrderBy方法,然后可以将其编译并提供给OrderBy调用。 ,如示例所示,因此是更直观,更简单的解决方案:

Creating Expression: 创建表达式:

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

How to Call: 通话方式:

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

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

ProductExpression.Compile() above will compile into x => x.Name , where column name is supplied at the run-time 上面的ProductExpression.Compile()将编译为x => x.Name ,其中在运行时提供列名

Please note in case the ordering field can be other types beside string data type, then make that also generic and supply it while calling extension method, only condition being property being called shall have the same type as supplied value, else it will be a run-time exception, while creating Expression 请注意,如果排序字段可以是字符串数据类型以外的其他类型,则在调用扩展方法时也使该字段通用并提供它,只有被调用的条件必须与提供的值具有相同的类型,否则将运行创建表达式时出现时间异常

Edit 1, how to make the OrderType field also generic 编辑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;
    }

How to call: 通话方式:

Now both the types need to be explicitly supplied, earlier were using generic type inference from IEnumerable<T> : 现在,这两种类型都需要显式提供,之前使用的是IEnumerable<T>泛型类型推断:

// For Integer Id field //对于整数ID字段

var ProductExpression = records.OrderByFunc<Product,int>("Id");

// For string name field //对于字符串名称字段

var ProductExpression = records.OrderByFunc<Product,string>("Name");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM