¿Cómo aplico OrderBy en un IQueryable usando un nombre de columna de cadena dentro de un método de extensión genérico?

public static IQueryable ApplySortFilter(this IQueryable query, string columnName) where T : EntityObject { var param = Expression.Parameter(typeof(T), "o"); var body = Expression.PropertyOrField(param,columnName); var sortExpression = Expression.Lambda(body, param); return query.OrderBy(sortExpression); } 

Debido a que el tipo de OrderBy no se infiere de sortExpression, debo especificar algo como esto en tiempo de ejecución:

 var sortExpression = Expression.Lambda(body, param); 

O

 return query.OrderBy(sortExpression); 

Sin embargo, no creo que esto sea posible ya que TSortColumn solo se puede determinar durante el tiempo de ejecución.

¿Hay alguna forma de evitar esto?

Hicimos algo similar (no 100% igual, pero similar) en un proyecto LINQ to SQL. Aquí está el código:

 public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) { var type = typeof(T); var property = type.GetProperty(ordering); var parameter = Expression.Parameter(type, "p"); var propertyAccess = Expression.MakeMemberAccess(parameter, property); var orderByExp = Expression.Lambda(propertyAccess, parameter); MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp)); return source.Provider.CreateQuery(resultExp); } 

En realidad, no usamos un genérico, teníamos una clase conocida, pero debería funcionar de forma genérica (he puesto el marcador de posición genérico donde debería estar).

Editar: para orden descendente, pase en OrderByDescending en lugar de “OrderBy”:

 MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp)); 

También puedes usar Dynamic Linq

Información aquí http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Descarga de C # aquí http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx

Luego solo agrega el uso de Linq.Dynamic; y automáticamente obtienes 2 métodos de extensión adicionales que se pueden usar así

 return query.OrderBy("StringColumnName"); 

Extendí tus funciones para agregar compatibilidad con las propiedades secundarias.

 private static LambdaExpression GenerateSelector(String propertyName, out Type resultType) where TEntity : class { // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). var parameter = Expression.Parameter(typeof(TEntity), "Entity"); // create the selector part, but support child properties PropertyInfo property; Expression propertyAccess; if (propertyName.Contains('.')) { // support to be sorted on child fields. String[] childProperties = propertyName.Split('.'); property = typeof(TEntity).GetProperty(childProperties[0]); propertyAccess = Expression.MakeMemberAccess(parameter, property); for (int i = 1; i < childProperties.Length; i++) { property = property.PropertyType.GetProperty(childProperties[i]); propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); } } else { property = typeof(TEntity).GetProperty(propertyName); propertyAccess = Expression.MakeMemberAccess(parameter, property); } resultType = property.PropertyType; // Create the order by expression. return Expression.Lambda(propertyAccess, parameter); } private static MethodCallExpression GenerateMethodCall(IQueryable source, string methodName, String fieldName) where TEntity : class { Type type = typeof(TEntity); Type selectorResultType; LambdaExpression selector = GenerateSelector(fieldName, out selectorResultType); MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, selectorResultType }, source.Expression, Expression.Quote(selector)); return resultExp; } 

Puede usar estas funciones como:

 GenerateMethodCall(source, "OrderByDescending", fieldName); 

Usé tu idea para el método de extensión para OrderBy. Pero en el caso de “muchos a muchos”, recibo un error. Por ejemplo, tiene la tabla Sitio, Cliente y Sitio_del_cliente. Para un sitio determinado, quiero ordenar por nombre de cliente y en extensión OrderBy (cuando paso “site.customer” donde el cliente es propiedad de navegación) obtengo un error en la línea: propertyAccess = Expression.MakeMemberAccess (propertyAccess, property);

Esto es lo que uso (con algunas mejoras :-)):

 public static IQueryable OrderBy(this IQueryable source, string orderByValues) where TEntity : class { IQueryable returnValue = null; string orderPair = orderByValues.Trim().Split(',')[0]; string command = orderPair.ToUpper().Contains("DESC") ? "OrderByDescending" : "OrderBy"; var type = typeof(TEntity); var parameter = Expression.Parameter(type, "p"); string propertyName = (orderPair.Split(' ')[0]).Trim(); System.Reflection.PropertyInfo property; MemberExpression propertyAccess; if (propertyName.Contains('.')) { // support to be sorted on child fields. String[] childProperties = propertyName.Split('.'); property = typeof(TEntity).GetProperty(childProperties[0]); propertyAccess = Expression.MakeMemberAccess(parameter, property); for (int i = 1; i < childProperties.Length; i++) { Type t = property.PropertyType; if (!t.IsGenericType) { property = t.GetProperty(childProperties[i]); } else { property = t.GetGenericArguments().First().GetProperty(childProperties[i]); } propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); } } else { property = type.GetProperty(propertyName); 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)); returnValue = source.Provider.CreateQuery(resultExpression); if (orderByValues.Trim().Split(',').Count() > 1) { // remove first item string newSearchForWords = orderByValues.ToString().Remove(0, orderByValues.ToString().IndexOf(',') + 1); return source.OrderBy(newSearchForWords); } return returnValue; } 

Saludos

Slobodan

Parece que esta es la manera de hacerlo, ahora para verificar que:

 // ***** OrderBy(company => company) ***** // Create an expression tree that represents the expression // 'whereCallExpression.OrderBy(company => company)' MethodCallExpression orderByCallExpression = Expression.Call( typeof(Queryable), "OrderBy", new Type[] { queryableData.ElementType, queryableData.ElementType }, whereCallExpression, Expression.Lambda>(pe, new ParameterExpression[] { pe })); // ***** End OrderBy ***** 

Si puede agregar el paquete “System.Linq.Dynamic” entonces, Demasiado fácil sin ninguna complicación,

El paquete fisrt insatll “System.Linq.Dynamic” del administrador de paquetes NuGet luego prueba lo que necesites a continuación.

Ex:

 public IQueryable GetWithInclude(Expression> predicate, List sortBy, int pageNo, int pageSize = 12, params string[] include) { try { var numberOfRecordsToSkip = pageNo * pageSize; var dynamic = DbSet.AsQueryable(); foreach (var s in include) { dynamic.Include(s); } return dynamic.OrderBy("CreatedDate").Skip(numberOfRecordsToSkip).Take(pageSize); } catch (Exception e) { throw new Exception(e.Message); } } 

Espero que esto ayude

Repare este código un poco: https://stackoverflow.com/a/1670085/5852630

Este código funciona con clasificación secuencial: primero ejecuta “OrderBy”, luego “ThenBy” (¡No “OrderBy”!)

 public static IQueryable OrderBy(this IQueryable source, string orderByValues) where TEntity : class { IQueryable returnValue = null; string[] orderPairs = orderByValues.Trim().Split(','); Expression resultExpression = source.Expression; string strAsc = "OrderBy"; string strDesc = "OrderByDescending"; foreach (string orderPair in orderPairs) { if (string.IsNullOrWhiteSpace(orderPair)) continue; string[] orderPairArr = orderPair.Trim().Split(' '); string propertyName = orderPairArr[0].Trim(); string orderNarrow = orderPairArr.Length > 1 ? orderPairArr[1].Trim() : string.Empty; string command = orderNarrow.ToUpper().Contains("DESC") ? strDesc : strAsc; Type type = typeof(TEntity); ParameterExpression parameter = Expression.Parameter(type, "p"); System.Reflection.PropertyInfo property; Expression propertyAccess; if (propertyName.Contains('.')) { // support to be sorted on child fields. String[] childProperties = propertyName.Split('.'); property = typeof(TEntity).GetProperty(childProperties[0]); propertyAccess = Expression.MakeMemberAccess(parameter, property); for (int i = 1; i < childProperties.Length; i++) { Type t = property.PropertyType; if (!t.IsGenericType) { property = t.GetProperty(childProperties[i]); } else { property = t.GetGenericArguments().First().GetProperty(childProperties[i]); } propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); } } else { property = type.GetProperty(propertyName); propertyAccess = Expression.MakeMemberAccess(parameter, property); } if (property.PropertyType == typeof(object)) { propertyAccess = Expression.Call(propertyAccess, "ToString", null); } LambdaExpression orderByExpression = Expression.Lambda(propertyAccess, parameter); resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType == typeof(object) ? typeof(string) : property.PropertyType }, resultExpression, Expression.Quote(orderByExpression)); strAsc = "ThenBy"; strDesc = "ThenByDescending"; } returnValue = source.Provider.CreateQuery(resultExpression); return returnValue; }