Jak określić argument przekazany do orderby
przy użyciu wartości, którą biorę jako parametr?
Dawny:
List<Student> existingStudends = new List<Student>{ new Student {...}, new Student {...}}
Obecnie wdrażane:
List<Student> orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList();
Zamiast tego c.Address
, jak mogę przyjąć to jako parametr?
Przykład
string param = "City";
List<Student> orderbyAddress = existingStudends.OrderByDescending(c => param).ToList();
OrderBy
jest cechą Linq, i jest onIEnumerable
, a nie cecha specyficzneList
. Zapraszam do cofnięcia edycji lub dalszej zmiany :)Odpowiedzi:
Oto możliwość wykorzystania refleksji ...
var param = "Address"; var propertyInfo = typeof(Student).GetProperty(param); var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null));
źródło
ThenBy
metody .Możesz użyć odrobiny refleksji, aby skonstruować drzewo wyrażeń w następujący sposób (jest to metoda rozszerzająca):
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> 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<TEntity>(resultExpression); }
orderByProperty
to nazwa właściwości, według której chcesz uporządkować i jeśli zostanie przekazana wartość true jako parametr dladesc
, zostanie posortowana w porządku malejącym; w przeciwnym razie posortuje w kolejności rosnącej.Teraz powinieneś być w stanie zrobić
existingStudents.OrderBy("City",true);
lubexistingStudents.OrderBy("City",false);
źródło
Aby rozwinąć odpowiedź przez @Icarus : jeśli chcesz, aby typ zwracany metody rozszerzającej był IOrderedQueryable zamiast IQueryable, możesz po prostu rzutować wynik w następujący sposób:
public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> 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<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression); }
źródło
1) Zainstaluj System.Linq.Dynamic
2) Dodaj następujący kod
public static class OrderUtils { public static string ToStringForOrdering<T, TKey>(this Expression<Func<T, TKey>> 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) Napisz swój przełącznik do wyboru funkcji Lambda
public static class SortHelper { public static Expression<Func<UserApp, object>> 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) Użyj swoich pomocników
Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering())
5) Możesz go używać ze stronicowaniem ( PagedList )
public virtual IPagedList<T> GetPage<TOrder>(Page page, Expression<Func<T, bool>> where, Expression<Func<T, TOrder>> order, bool isDesc = false, params Expression<Func<T, object>>[] 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<T>(results, page.PageNumber, page.PageSize, total); }
Wyjaśnienie
System.Linq.Dynamic pozwala nam ustawić wartość ciągu w metodzie OrderBy. Ale wewnątrz tego rozszerzenia ciąg zostanie przeanalizowany do Lambda. Pomyślałem więc, że zadziała, jeśli przeanalizujemy Lambdę na łańcuch i przekażemy go do metody OrderBy. I to działa!
źródło
private Func<T, object> GetOrderByExpression<T>(string sortColumn) { Func<T, object> 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<T> OrderByDir<T>(IEnumerable<T> source, string dir, Func<T, object> OrderByColumn) { return dir.ToUpper() == "ASC" ? source.OrderBy(OrderByColumn).ToList() : source.OrderByDescending(OrderByColumn).ToList();`` } // Call the code like below var orderByExpression= GetOrderByExpression<SearchResultsType>(sort); var data = OrderByDir<SponsorSearchResults>(resultRecords, SortDirectionString, orderByExpression);
źródło
Oto coś, co wymyśliłem, aby radzić sobie z warunkowym spadkiem. Można to połączyć z innymi metodami
keySelector
dynamicznego generowania funkcji.public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector, System.ComponentModel.ListSortDirection sortOrder ) { if (sortOrder == System.ComponentModel.ListSortDirection.Ascending) return source.OrderBy(keySelector); else return source.OrderByDescending(keySelector); }
Stosowanie:
//imagine this is some parameter var direction = System.ComponentModel.ListSortDirection.Ascending; query = query.OrderBy(ec => ec.MyColumnName, direction);
Zauważ, że pozwala to połączyć to
.OrderBy
rozszerzenie z nowym parametrem do dowolnego 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);
źródło
To nie pozwala ci przejść
string
, jak prosiłeś w swoim pytaniu, ale może nadal działać dla ciebie.OrderByDescending
Metoda trwaFunc<TSource, TKey>
, więc można przepisać swoją funkcję w ten sposób:List<Student> QueryStudents<TKey>(Func<Student, TKey> orderBy) { return existingStudents.OrderByDescending(orderBy).ToList(); }
Istnieją również inne przeciążenia,
OrderByDescending
które pobierają aExpression<Func<TSource, TKey>>
i / lub aIComparer<TKey>
. Możesz również zajrzeć do nich i sprawdzić, czy dają ci coś pożytecznego.źródło
Jedyne rozwiązanie, które dla mnie zadziałało, zostało opublikowane tutaj https://gist.github.com/neoGeneva/1878868 przez neoGeneva.
Ponownie prześlę jego kod, ponieważ działa dobrze i nie chciałbym, aby zgubił się w interwebach!
public static IQueryable<T> OrderBy<T>(this IQueryable<T> 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<T>)sorter .Invoke(null, new object[] { source, sortLambda }); } return source; }
źródło
Dodaj pakiet użytkowy Dynamite do swojego kodu
Dodaj przestrzeń nazw Dynamite.Extensions Np .: using Dynamite.Extensions;
Podaj kolejność za pomocą zapytania, tak jak w przypadku każdego zapytania SQL, np. Studentów.OrderBy ("Miasto DESC, Adres").
źródło
Aby przedłużyć odpowiedź @Icarus: jeśli chcesz posortować według dwóch pól, mogę wykonać następującą funkcję (dla jednego pola odpowiedź Icariusa działa bardzo dobrze).
public static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> q, string SortField1, string SortField2, bool Ascending) { var param = Expression.Parameter(typeof(T), "p"); var body = GetBodyExp(SortField1, SortField2, param); var exp = Expression.Lambda(body, param); string method = Ascending ? "OrderBy" : "OrderByDescending"; Type[] types = new Type[] { q.ElementType, exp.Body.Type }; var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); return q.Provider.CreateQuery<T>(mce); }
Jest to funkcja, którą ciało zwraca dla wyrażenia lambda, działa z stringiem i int, ale wystarczy dodać więcej typów, aby działała zgodnie z potrzebami każdego programisty
public static NewExpression GetBodyExp(string field1, string field2, ParameterExpression Parametro) { // SE OBTIENE LOS NOMBRES DE LOS TIPOS DE VARIABLE string TypeName1 = Expression.Property(Parametro, field1).Type.Name; string TypeName2 = Expression.Property(Parametro, field2).Type.Name; // SE DECLARA EL TIPO ANONIMO SEGUN LOS TIPOS DE VARIABLES Type TypeAnonymous = null; if (TypeName1 == "String") { string var1 = "0"; if (TypeName2 == "Int32") { int var2 = 0; var example = new { var1, var2 }; TypeAnonymous = example.GetType(); } if (TypeName2 == "String") { string var2 = "0"; var example = new { var1, var2 }; TypeAnonymous = example.GetType(); } } if (TypeName1 == "Int32") { int var1 = 0; if (TypeName2 == "Int32") { string var2 = "0"; var example = new { var1, var2 }; TypeAnonymous = example.GetType(); } if (TypeName2 == "String") { string var2 = "0"; var example = new { var1, var2 }; TypeAnonymous = example.GetType(); } } //se declaran los TIPOS NECESARIOS PARA GENERAR EL BODY DE LA EXPRESION LAMBDA MemberExpression[] args = new[] { Expression.PropertyOrField(Parametro, field1), Expression.PropertyOrField(Parametro, field2) }; ConstructorInfo CInfo = TypeAnonymous.GetConstructors()[0]; IEnumerable<MemberInfo> a = TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property); //BODY NewExpression body = Expression.New(CInfo, args, TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property)); return body; }
Aby go użyć, należy wykonać następujące czynności
IQueryable<MyClass> IqMyClass= context.MyClass.AsQueryable(); List<MyClass> ListMyClass= IqMyClass.OrderByDynamic("UserName", "IdMyClass", true).ToList();
jeśli istnieje lepszy sposób na zrobienie tego, byłoby wspaniale, gdyby się nim podzielili
Udało mi się go rozwiązać dzięki: Jak mogę zrobić wyrażenie lambda z wieloma właściwościami za pomocą Linq
źródło
Spóźniłem się na imprezę, ale żadne z tych rozwiązań nie zadziałało. Bardzo chciałem wypróbować System.Linq.Dynamic, ale nie mogłem go znaleźć na Nuget, może zdeprecjonowany? Tak czy inaczej...
Oto rozwiązania, które wymyśliłem. Musiałem dynamicznie używać mieszanki OrderBy , OrderByDescending i OrderBy> ThenBy .
Po prostu stworzyłem metodę rozszerzenia dla mojego obiektu listy, trochę zepsutą, wiem ... Nie polecałbym tego, gdyby było to coś, co robię dużo, ale jest to dobre na jeden z nich.
List<Employee> Employees = GetAllEmployees(); foreach(Employee oEmployee in Employees.ApplyDynamicSort(eEmployeeSort)) { //do stuff } public static IOrderedEnumerable<Employee> ApplyDynamicSort(this List<Employee> 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); } }
źródło