Based on API, I can have multiple parameters which can be used in order by
. There is a function which creates a dynamic order by
parameter as a string. I want to use this in .OrderBy
but not sure how to do this.
API Call:
{{url}}?keyword=singer&page=12&size=5&sortby=LastName&sortby=FirstName
Code:
public CallCenterPageResult<CallCenterCustomerSummary> GetCustomers(int page, int pageSize, IEnumerable<SortParameter> sortParameters, string keyword)
{
using (var ctx = new EFCallCenterContext())
{
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(sortParameters.ToOrderBy()) // "LastName ASC, FirstName ASC"
.Skip(pageSize * (page - 1)).Take(pageSize)
.ToList();
return customerDetails;
}
}
Extension Method to get order by:
static class RepositoryExtensions
{
public static string ToOrderBy(this IEnumerable<SortParameter> parameters)
{
return string.Join(", ", parameters.Select(p => p.SortBy + (p.Descending ? " DESC" : " ASC")));
}
}
Output:
"LastName ASC, FirstName ASC"
Extension method to accept dynamic LINQ:
public static class Extension
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
var props = property.Split('.');
var type = typeof(T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi); // Errors out here.
type = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
}
Error:
System.ArgumentNullException: Value cannot be null.
Parameter name: property
This is the first time working with this complex query so not sure how to do this. I can add more info if needed.