[c#] 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);


답변

@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);
}


답변

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 메서드에 제공하면 작동 할 것이라고 생각했습니다. 그리고 작동합니다!


답변

   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>