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());
}
}
Check the sample code for the solution:
Result
Created an expression using extension method on the
Queryable
type, which internally callsOrderBy
method of theQueryable
type, expectingIQueryable
to be the Input, along with the field name and thus runs the ordering function and Ordered collection is the final OutputThis may fit your use case better, here instead of calling
OrderBy
method, we are creating theExpression<Func<T,string>>
as an extension method to theIEnumerable<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:ProductExpression.Compile()
above will compile intox => x.Name
, where column name is supplied at the run-timePlease 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
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");