Linq OrderBy 인수를 어떻게 동적으로 지정합니까?
orderby
매개 변수로받는 값 을 사용하여 전달 된 인수를 어떻게 지정 합니까?
전의:
List<Student> existingStudends = new List<Student>{ new Student {...}, new Student {...}}
현재 구현 :
List<Student> orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList();
대신 c.Address
매개 변수로 어떻게 사용할 수 있습니까?
예
string param = "City";
List<Student> orderbyAddress = existingStudends.OrderByDescending(c => param).ToList();
여기에 반사를 사용하는 가능성이 있습니다 ...
var param = "Address";
var propertyInfo = typeof(Student).GetProperty(param);
var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null));
약간의 리플렉션을 사용하여 다음과 같이 표현식 트리를 구성 할 수 있습니다 (확장 방법입니다).
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
정렬 할 속성 이름이며에 대한 매개 변수로 true를 전달하면 desc
내림차순으로 정렬됩니다. 그렇지 않으면 오름차순으로 정렬됩니다.
이제 할 수 existingStudents.OrderBy("City",true);
있거나existingStudents.OrderBy("City",false);
1) System.Linq.Dynamic 설치
2) 다음 코드 추가
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) 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) 도우미 사용
Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering())
5) pagging ( 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);
}
설명
System.Linq.Dynamic을 사용하면 OrderBy 메서드에서 문자열 값을 설정할 수 있습니다. 그러나이 확장 내에서 문자열은 Lambda로 구문 분석됩니다. 그래서 Lambda를 문자열로 구문 분석하고 OrderBy 메서드에 제공하면 작동 할 것이라고 생각했습니다. 그리고 작동합니다!
@Icarus 의 답변 을 확장하려면 확장 메서드의 반환 유형이 IQueryable 대신 IOrderedQueryable이되도록하려면 다음과 같이 결과를 간단히 캐스팅 할 수 있습니다.
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);
}
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);
조건부 내림차순을 처리하기 위해 제가 생각해 낸 것이 있습니다. 이를 keySelector
동적으로 func 를 생성하는 다른 방법과 결합 할 수 있습니다.
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);
}
용법:
//imagine this is some parameter
var direction = System.ComponentModel.ListSortDirection.Ascending;
query = query.OrderBy(ec => ec.MyColumnName, direction);
이렇게하면 .OrderBy
새 매개 변수를 사용 하여이 확장을 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);
string
질문에서 요청한대로 를 통과 할 수는 없지만 여전히 작동 할 수 있습니다.
이 OrderByDescending
메서드는를 사용 Func<TSource, TKey>
하므로 다음과 같이 함수를 다시 작성할 수 있습니다.
List<Student> QueryStudents<TKey>(Func<Student, TKey> orderBy)
{
return existingStudents.OrderByDescending(orderBy).ToList();
}
, 및 / 또는 OrderByDescending
을 취하는 다른 오버로드 도 있습니다 . 당신은 또한 그것들을 조사하고 그들이 당신에게 유용한 것을 제공하는지 볼 수 있습니다.Expression<Func<TSource, TKey>>
IComparer<TKey>
나를 위해 일한 유일한 솔루션은 neoGeneva가 https://gist.github.com/neoGeneva/1878868 에 게시했습니다 .
나는 그의 코드가 잘 작동하고 인터 웹에서 손실되는 것을 원하지 않기 때문에 다시 게시 할 것입니다!
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;
}
@Icarus의 응답을 확장하려면 : 두 필드로 정렬하려면 다음 기능을 수행 할 수 있습니다 (한 필드에 대해 Icarius의 응답이 매우 잘 작동 함).
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);
}
This is the function that the body returns for the lambda expression, it works with string and int, but it is enough to add more types to make it work according to the need of each programmer
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;
}
to use it the following is done
IQueryable<MyClass> IqMyClass= context.MyClass.AsQueryable();
List<MyClass> ListMyClass= IqMyClass.OrderByDynamic("UserName", "IdMyClass", true).ToList();
if there is a better way to do this, it would be great if they share it
I managed to solve it thanks to: How can I make a Multiple property lambda expression with Linq
I'm way late to the party but none of these solutions worked for me. I was eager to try System.Linq.Dynamic, but I couldn't find that on Nuget, maybe depreciated? Either way...
Here is a solutions I came up with. I needed to dynamically use a mixture of OrderBy, OrderByDescending and OrderBy > ThenBy.
I simply created an extension method for my list object, a bit hacky I know... I wouldn't recommend this if it were something I was doing a lot of, but it's good for a one off.
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);
}
}
Add the nugget package Dynamite to your code
Add the namespace Dynamite.Extensions Eg : using Dynamite.Extensions;
Give Order by query like any SQL query Eg : students.OrderBy(" City DESC, Address").ToList();
참고URL : https://stackoverflow.com/questions/7265186/how-do-i-specify-the-linq-orderby-argument-dynamically
'development' 카테고리의 다른 글
Null이있는 SQL Server 문자열 연결 (0) | 2020.10.06 |
---|---|
Android에서 뷰를 애니메이션하고 새 위치 / 크기를 유지하려면 어떻게해야합니까? (0) | 2020.10.06 |
문자열에서 특수 문자를 바꾸는 방법은 무엇입니까? (0) | 2020.10.06 |
“0”과“1”을 거짓과 참으로 변환하는 방법 (0) | 2020.10.06 |
UITapGestureRecognizer는 self.view를 탭하지만 하위보기는 무시합니다. (0) | 2020.10.06 |