public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> 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);
}
Parce que le type pour OrderBy n'est pas déduit de sortExpression, je dois le spécifier quelque chose comme ça au moment de l'exécution:
var sortExpression = Expression.Lambda<T, TSortColumn>(body, param);
Ou
return query.OrderBy<T, TSortColumn>(sortExpression);
Cependant, je ne pense pas que cela soit possible car TSortColumn ne peut être déterminé que pendant l'exécution.
Y a-t-il un moyen de contourner ceci?
Réponses:
Nous avons fait quelque chose de similaire (pas 100% identique, mais similaire) dans un projet LINQ to SQL. Voici le code:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> 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<T>(resultExp); }
Nous n'avons pas réellement utilisé de générique, nous avions une classe connue, mais cela devrait fonctionner sur un générique (j'ai mis l'espace réservé générique là où il devrait être).
Edit: Par ordre décroissant, passez au
OrderByDescending
lieu de "OrderBy":MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
la source
values
?Vous pouvez également utiliser Dynamic Linq
Infos ici http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Téléchargez C # ici http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx
Ensuite, ajoutez simplement le using Linq.Dynamic; et vous obtenez automatiquement 2 méthodes d'extension supplémentaires qui peuvent être utilisées comme ceci
return query.OrderBy("StringColumnName");
la source
J'ai étendu vos fonctions pour ajouter la prise en charge des propriétés enfants.
private static LambdaExpression GenerateSelector<TEntity>(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<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName) where TEntity : class { Type type = typeof(TEntity); Type selectorResultType; LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType); MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, selectorResultType }, source.Expression, Expression.Quote(selector)); return resultExp; }
Vous pouvez utiliser ces fonctions comme:
GenerateMethodCall<TEntity>(source, "OrderByDescending", fieldName);
la source
J'ai utilisé votre idée de méthode d'extension pour OrderBy. Mais en cas de "plusieurs à plusieurs", j'obtiens une erreur. Par exemple, vous avez la table Site, Customer et Customer_site. Pour un site donné, je veux trier par nom de client et dans l'extension OrderBy (lorsque je passe "site.customer" où le client est la propriété de navigation), j'obtiens une erreur en ligne: propertyAccess = Expression.MakeMemberAccess (propertyAccess, property);
C'est ce que j'utilise (avec quelques améliorations :-)):
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class { IQueryable<TEntity> 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<TEntity>(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; }
Cordialement
Slobodan
la source
Il semble que ce soit la façon de le faire, maintenant pour vérifier 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<Func<string, string>>(pe, new ParameterExpression[] { pe })); // ***** End OrderBy *****
la source
Si vous pouvez ajouter le package "System.Linq.Dynamic" alors, Trop facile sans aucune complication,
premier package insatll "System.Linq.Dynamic" du gestionnaire de packages NuGet, puis essayez comme ci-dessous selon vos besoins,
Ex:
public IQueryable<TEntity> GetWithInclude(Expression<Func<TEntity, bool>> predicate, List<string> 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); } }
J'espère que cela aidera
la source
J'ai corrigé un peu ce code: https://stackoverflow.com/a/1670085/5852630
Ce code fonctionne avec un tri séquentiel: exécutez d'abord "OrderBy", puis "ThenBy" (pas "OrderBy"!)
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class { IQueryable<TEntity> 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<TEntity>(resultExpression); return returnValue; }
la source
Voici mon adaptation de la réponse de @Davy Landman (je voulais une méthode d'extension) et j'ai simplifié un peu.
public static IQueryable<T> SortBy<T>(this IQueryable<T> source, String propertyName, WebControls.SortDirection direction) { if (source == null) throw new ArgumentNullException("source"); if (String.IsNullOrEmpty(propertyName)) return source; // Create a parameter to pass into the Lambda expression //(Entity => Entity.OrderByField). var parameter = Expression.Parameter(typeof(T), "Entity"); // create the selector part, but support child properties (it works without . too) String[] childProperties = propertyName.Split('.'); MemberExpression property = Expression.Property(parameter, childProperties[0]); for (int i = 1; i < childProperties.Length; i++) { property = Expression.Property(property, childProperties[i]); } LambdaExpression selector = Expression.Lambda(property, parameter); string methodName = (direction > 0) ? "OrderByDescending" : "OrderBy"; MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { source.ElementType, property.Type }, source.Expression, Expression.Quote(selector)); return source.Provider.CreateQuery<T>(resultExp); }
Il peut être utilisé comme ceci:
gridview1.DataSource = DbContext.TB_CARS.SortBy("model", SortDirection.Descending); //OR gridview1.DataSource = DbContext.TB_CARS.SortBy("owner.first_name", 0);
la source