Net Core Linq中用于扩展方法的OrderBy表达式树 [英] OrderBy Expression Tree in Net Core Linq for Extension Method

查看:114
本文介绍了Net Core Linq中用于扩展方法的OrderBy表达式树的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个扩展方法来模仿它,


工作原理:


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


选项2:


这可能适合您的使用更好的情况是,我们在这里创建 Expression< Func< T,string>> 而不是调用 OrderBy 方法作为 I的扩展方法Enumerable< T> ,然后可以将其编译并提供给OrderBy调用,如示例中所示,因此是更加直观和简单的解决方案:


创建表达式:




  public static class ExpressionTreesExtesion 
{
public static Expression< Func< T,string>> OrderByExpression< T>(此IEnumerable< T"可枚举的字符串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);
退货orderExpression;
}
}




如何致电:




  var ProductExpression = records.OrderByExpression( Name) ; 

var结果= records.OrderBy(ProductExpression.Compile());

ProductExpression.Compile()将进行编译进入 x => x.Name ,其中在运行时提供列名



请注意,如果排序字段可以是字符串数据以外的其他类型类型,然后使其也通用,并在调用扩展方法时提供它,仅条件被调用的属性应与提供的值具有相同的类型,否则将在创建Expression

$ b $时是运行时异常b


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




 公共静态表达式< Func< T,TField> OrderByFunc< T,TField>(此IEnumerable< T"可枚举,字符串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);
退货orderExpression;
}




如何拨打电话:


现在这两种类型都需要明确提供,更早之前使用的是来自 IEnumerable<的通用类型推断。 T>



//对于整数ID字段



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



//对于字符串名称字段



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


I want to create an Extension method which mimics this, 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?

tried adding following and did not work .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:

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

解决方案

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

Option 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:

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

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

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>:

// For Integer Id field

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

// For string name field

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

这篇关于Net Core Linq中用于扩展方法的OrderBy表达式树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆