[c#] 속성 이름을 문자열로 사용하여 속성별로 정렬하는 코드

속성 이름을 문자열로 사용할 때 C #의 속성에 대해 코딩하는 가장 간단한 방법은 무엇입니까? 예를 들어 사용자가 LINQ를 사용하여 선택한 속성별로 일부 검색 결과를 정렬 할 수 있도록하고 싶습니다. UI의 “order by”속성을 문자열 값으로 선택합니다. 조건부 논리 (if / else, switch)를 사용하여 문자열을 속성에 매핑하지 않고도 해당 문자열을 linq 쿼리의 속성으로 직접 사용할 수있는 방법이 있습니까? 반사?

논리적으로 이것은 내가하고 싶은 것입니다.

query = query.OrderBy(x => x."ProductId");

업데이트 : 원래 Linq to Entities를 사용하고 있음을 지정하지 않았습니다. 반영 (적어도 GetProperty, GetValue 접근 방식)이 L2E로 변환되지 않는 것 같습니다.



답변

나는 다른 사람들이 게시 한 것에이 대안을 제공 할 것입니다.

System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");

query = query.OrderBy(x => prop.GetValue(x, null));

이렇게하면 속성을 얻기 위해 리플렉션 API에 대한 반복적 인 호출이 방지됩니다. 이제 유일한 반복 호출은 값을 얻는 것입니다.

하나

PropertyDescriptor대신에 a 를 사용하는 것이 좋습니다. 이렇게하면 사용자 TypeDescriptor지정을 유형에 할당 할 수 있으므로 속성과 값을 검색하기위한 간단한 작업을 수행 할 수 있습니다. 사용자 지정 설명자가 없으면 어쨌든 리플렉션으로 돌아갑니다.

PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName");

query = query.OrderBy(x => prop.GetValue(x));

속도를 높이려면 HyperDescriptorCodeProject에 대한 Marc Gravel의 프로젝트를 확인하십시오 . 저는 이것을 아주 성공적으로 사용했습니다. 비즈니스 객체에 대한 고성능 데이터 바인딩 및 동적 속성 작업을위한 생명의 은인입니다.


답변

파티에 조금 늦었지만 도움이 되었으면합니다.

리플렉션을 사용할 때의 문제는 결과 식 트리가 내부 .Net 공급자가 아닌 다른 Linq 공급자에서 거의 확실히 지원되지 않는다는 것입니다. 내부 컬렉션에는 괜찮지 만 페이지 매김 전에 소스 (SQL, MongoDb 등)에서 정렬이 수행되는 경우에는 작동하지 않습니다.

아래 코드 샘플은 OrderBy 및 OrderByDescending에 대한 IQueryable 확장 메서드를 제공하며 다음과 같이 사용할 수 있습니다.

query = query.OrderBy("ProductId");

연장 방법 :

public static class IQueryableExtensions
{
    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName)
    {
        return source.OrderBy(ToLambda<T>(propertyName));
    }

    public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName)
    {
        return source.OrderByDescending(ToLambda<T>(propertyName));
    }

    private static Expression<Func<T, object>> ToLambda<T>(string propertyName)
    {
        var parameter = Expression.Parameter(typeof(T));
        var property = Expression.Property(parameter, propertyName);
        var propAsObject = Expression.Convert(property, typeof(object));

        return Expression.Lambda<Func<T, object>>(propAsObject, parameter);
    }
}

감사합니다, 마크.


답변

나는에서 답 좋아 @ 마크 파월 ,하지만 같은 @ShuberFu가 말했다, 그것은 오류를 제공합니다LINQ to Entities only supports casting EDM primitive or enumeration types .

풀이 var propAsObject = Expression.Convert(property, typeof(object)); 는 정수와 같은 값 유형 인 속성에서 작동하지 않았습니다. 묵시적으로 int를 개체로 상자에 넣지 않기 때문입니다.

Kristofer AnderssonMarc Gravell의 아이디어를 사용하여 속성 이름을 사용하여 Queryable 함수를 생성하고 Entity Framework에서 계속 작동하도록하는 방법을 찾았습니다. 또한 선택적 IComparer 매개 변수를 포함했습니다. 주의: IComparer 매개 변수는 Entity Framework에서 작동하지 않으며 Linq to Sql을 사용하는 경우 생략해야합니다.

다음은 Entity Framework 및 Linq to Sql에서 작동합니다.

query = query.OrderBy("ProductId");

그리고 @ 사이먼 SCHEURER는 이 또한 작동합니다 :

query = query.OrderBy("ProductCategory.CategoryId");

Entity Framework 또는 Linq to Sql을 사용하지 않는 경우 다음과 같이 작동합니다.

query = query.OrderBy("ProductCategory", comparer);

다음은 코드입니다.

public static class IQueryableExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
    return CallOrderedQueryable(query, "OrderBy", propertyName, comparer);
}

public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
    return CallOrderedQueryable(query, "OrderByDescending", propertyName, comparer);
}

public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
    return CallOrderedQueryable(query, "ThenBy", propertyName, comparer);
}

public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
    return CallOrderedQueryable(query, "ThenByDescending", propertyName, comparer);
}

/// <summary>
/// Builds the Queryable functions using a TSource property name.
/// </summary>
public static IOrderedQueryable<T> CallOrderedQueryable<T>(this IQueryable<T> query, string methodName, string propertyName,
        IComparer<object> comparer = null)
{
    var param = Expression.Parameter(typeof(T), "x");

    var body = propertyName.Split('.').Aggregate<string, Expression>(param, Expression.PropertyOrField);

    return comparer != null
        ? (IOrderedQueryable<T>)query.Provider.CreateQuery(
            Expression.Call(
                typeof(Queryable),
                methodName,
                new[] { typeof(T), body.Type },
                query.Expression,
                Expression.Lambda(body, param),
                Expression.Constant(comparer)
            )
        )
        : (IOrderedQueryable<T>)query.Provider.CreateQuery(
            Expression.Call(
                typeof(Queryable),
                methodName,
                new[] { typeof(T), body.Type },
                query.Expression,
                Expression.Lambda(body, param)
            )
        );
}
}


답변

네, 리플렉션 외에 다른 방법은 없다고 생각합니다.

예:

query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));


답변

query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));

내 머리 꼭대기에서 정확한 구문을 기억하려고 노력하지만 그것이 옳다고 생각합니다.


답변

반성이 답입니다!

typeof(YourType).GetProperty("ProductId").GetValue(theInstance);

반영된 PropertyInfo를 캐시하고, 잘못된 문자열을 확인하고, 쿼리 비교 함수를 작성하기 위해 할 수있는 일이 많이 있지만, 그 핵심은 바로이 작업입니다.


답변

동적 Linq를 사용할 수 있습니다 . 블로그를 확인 하세요.

StackOverFlow 게시물 도 확인하십시오 .