¿Cómo especifico dinámicamente el argumento Linq OrderBy?

¿Cómo especifico el argumento pasado a orderby usando un valor que tomo como parámetro?

Ex:

 List existingStudends = new List{ new Student {...}, new Student {...}} 

Implementación actual:

 List orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList(); 

En lugar de c.Address , ¿cómo puedo tomar eso como un parámetro?

Ejemplo

  string param = "City"; List orderbyAddress = existingStudends.OrderByDescending(c => param).ToList(); 

Aquí hay una posibilidad usando la reflexión …

 var param = "Address"; var propertyInfo = typeof(Student).GetProperty(param); var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null)); 

Puede usar un poco de reflexión para construir el árbol de expresiones de la siguiente manera (este es un método de extensión):

 public static IQueryable OrderBy(this IQueryable source, string orderByProperty, bool desc) { string command = desc ? "OrderByDescending" : "OrderBy"; var type = typeof(TEntity); var property = type.GetProperty(orderByProperty); var parameter = Expression.Parameter(type, "p"); var propertyAccess = Expression.MakeMemberAccess(parameter, property); var orderByExpression = Expression.Lambda(propertyAccess, parameter); var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression)); return source.Provider.CreateQuery(resultExpression); } 

orderByProperty es el nombre de la propiedad que desea ordenar y si pasa true como parámetro para desc , ordenará en orden descendente; de lo contrario, se ordenarán en orden ascendente.

Ahora debería poder hacer existingStudents.OrderBy("City",true); o existingStudents.OrderBy("City",false);

  private Func GetOrderByExpression(string sortColumn) { Func orderByExpr = null; if (!String.IsNullOrEmpty(sortColumn)) { Type sponsorResultType = typeof(T); if (sponsorResultType.GetProperties().Any(prop => prop.Name == sortColumn)) { System.Reflection.PropertyInfo pinfo = sponsorResultType.GetProperty(sortColumn); orderByExpr = (data => pinfo.GetValue(data, null)); } } return orderByExpr; } public List OrderByDir(IEnumerable source, string dir, Func OrderByColumn) { return dir.ToUpper() == "ASC" ? source.OrderBy(OrderByColumn).ToList() : source.OrderByDescending(OrderByColumn).ToList();`` } // Call the code like below var orderByExpression= GetOrderByExpression(sort); var data = OrderByDir(resultRecords, SortDirectionString, orderByExpression); 

1) Instalar System.Linq.Dynamic

2) Agregue el siguiente código

 public static class OrderUtils { public static string ToStringForOrdering(this Expression> expression, bool isDesc = false) { var str = expression.Body.ToString(); var param = expression.Parameters.First().Name; str = str.Replace("Convert(", "(").Replace(param + ".", ""); return str + (isDesc ? " descending" : ""); } } 

3) Escribe tu interruptor para seleccionar la función Lambda

 public static class SortHelper { public static Expression> UserApp(string orderProperty) { orderProperty = orderProperty?.ToLowerInvariant(); switch (orderProperty) { case "firstname": return x => x.PersonalInfo.FirstName; case "lastname": return x => x.PersonalInfo.LastName; case "fullname": return x => x.PersonalInfo.FirstName + x.PersonalInfo.LastName; case "email": return x => x.Email; } } } 

4) Usa tus ayudantes

 Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering()) 

5) Puede usarlo con paginación ( PagedList )

 public virtual IPagedList GetPage(Page page, Expression> where, Expression> order, bool isDesc = false, params Expression>[] includes) { var orderedQueryable = Dbset.OrderBy(order.ToStringForOrdering(isDesc)); var query = orderedQueryable.Where(where).GetPage(page); query = AppendIncludes(query, includes); var results = query.ToList(); var total = Dbset.Count(where); return new StaticPagedList(results, page.PageNumber, page.PageSize, total); } 

Explicación

System.Linq.Dynamic nos permite establecer el valor de cadena en el método OrderBy. Pero dentro de esta extensión, la cadena se analizará en Lambda. Así que pensé que funcionaría si analizamos Lambda para enhebrarlo y dárselo al método OrderBy. ¡Y funciona!

Aquí hay algo que se me ocurrió para lidiar con un descenso descendente condicional. Puede combinar esto con otros métodos para generar el keySelector dinámicamente.

  public static IOrderedQueryable OrderBy(this IQueryable source, System.Linq.Expressions.Expression> keySelector, System.ComponentModel.ListSortDirection sortOrder ) { if (sortOrder == System.ComponentModel.ListSortDirection.Ascending) return source.OrderBy(keySelector); else return source.OrderByDescending(keySelector); } 

Uso:

 //imagine this is some parameter var direction = System.ComponentModel.ListSortDirection.Ascending; query = query.OrderBy(ec => ec.MyColumnName, direction); 

Observe que esto le permite encadenar esta extensión .OrderBy con un nuevo parámetro en cualquier IQueryable.

 // perhaps passed in as a request of user to change sort order // var direction = System.ComponentModel.ListSortDirection.Ascending; query = context.Orders .Where(o => o.Status == OrderStatus.Paid) .OrderBy(ec => ec.OrderPaidUtc, direction); 

Esto no le permite pasar una string , como lo pidió en su pregunta, pero aún podría funcionar para usted.

El método OrderByDescending tiene un Func , por lo que puede volver a escribir su función de esta manera:

 List QueryStudents(Func orderBy) { return existingStudents.OrderByDescending(orderBy).ToList(); } 

También existen otras sobrecargas para OrderByDescending que toman una Expression> , y / o un IComparer . También puede ver esos y ver si le proporcionan algo de uso.

La única solución que funcionó para mí fue publicada aquí https://gist.github.com/neoGeneva/1878868 por neoGeneva.

Volveré a publicar su código porque funciona bien y no me gustaría que se perdiera en el interwebs.

  public static IQueryable OrderBy(this IQueryable source, string sortExpression) { if (source == null) throw new ArgumentNullException("source", "source is null."); if (string.IsNullOrEmpty(sortExpression)) throw new ArgumentException("sortExpression is null or empty.", "sortExpression"); var parts = sortExpression.Split(' '); var isDescending = false; var propertyName = ""; var tType = typeof(T); if (parts.Length > 0 && parts[0] != "") { propertyName = parts[0]; if (parts.Length > 1) { isDescending = parts[1].ToLower().Contains("esc"); } PropertyInfo prop = tType.GetProperty(propertyName); if (prop == null) { throw new ArgumentException(string.Format("No property '{0}' on type '{1}'", propertyName, tType.Name)); } var funcType = typeof(Func< ,>) .MakeGenericType(tType, prop.PropertyType); var lambdaBuilder = typeof(Expression) .GetMethods() .First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2) .MakeGenericMethod(funcType); var parameter = Expression.Parameter(tType); var propExpress = Expression.Property(parameter, prop); var sortLambda = lambdaBuilder .Invoke(null, new object[] { propExpress, new ParameterExpression[] { parameter } }); var sorter = typeof(Queryable) .GetMethods() .FirstOrDefault(x => x.Name == (isDescending ? "OrderByDescending" : "OrderBy") && x.GetParameters().Length == 2) .MakeGenericMethod(new[] { tType, prop.PropertyType }); return (IQueryable)sorter .Invoke(null, new object[] { source, sortLambda }); } return source; } 

Para ampliar la respuesta con @Icarus : si desea que el tipo de devolución del método de extensión sea IOrderedQueryable en lugar de IQueryable, simplemente puede convertir el resultado de la siguiente manera:

 public static IOrderedQueryable OrderBy(this IQueryable source, string orderByProperty, bool desc) { string command = desc ? "OrderByDescending" : "OrderBy"; var type = typeof(TEntity); var property = type.GetProperty(orderByProperty); var parameter = Expression.Parameter(type, "p"); var propertyAccess = Expression.MakeMemberAccess(parameter, property); var orderByExpression = Expression.Lambda(propertyAccess, parameter); var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression)); return (IOrderedQueryable)source.Provider.CreateQuery(resultExpression); } 
  • Agregue el paquete de nugget Dynamite a su código

  • Agregue el espacio de nombre Dynamite.Extensions Ejemplo: usando Dynamite.Extensions;

  • Realice el pedido por consulta como cualquier consulta SQL. Por ejemplo: students.OrderBy (“City DESC, Address”). ToList ();

Llego tarde a la fiesta pero ninguna de estas soluciones funcionó para mí. Estaba ansioso por probar System.Linq.Dynamic, pero no pude encontrar eso en Nuget, ¿tal vez me deprecié? De cualquier manera…

Aquí hay una solución que se me ocurrió. Necesitaba usar dinámicamente una mezcla de OrderBy , OrderByDescending y OrderBy> ThenBy .

Simplemente creé un método de extensión para mi objeto de lista, un poco hacky, lo sé … No recomendaría esto si fuera algo que estuviese haciendo mucho, pero es bueno para uno.

 List Employees = GetAllEmployees(); foreach(Employee oEmployee in Employees.ApplyDynamicSort(eEmployeeSort)) { //do stuff } public static IOrderedEnumerable ApplyDynamicSort(this List lEmployees, Enums.EmployeeSort eEmployeeSort) { switch (eEmployeeSort) { case Enums.EmployeeSort.Name_ASC: return lEmployees.OrderBy(x => x.Name); case Enums.EmployeeSort.Name_DESC: return lEmployees.OrderByDescending(x => x.Name); case Enums.EmployeeSort.Department_ASC_Salary_DESC: return lEmployees.OrderBy(x => x.Department).ThenByDescending(y => y.Salary); default: return lEmployees.OrderBy(x => x.Name); } }