Program Tip

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

programtip 2020. 10. 25. 12:49
반응형

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


속성 이름을 문자열로 사용할 때 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);

There's lots of things you can do to cache the reflected PropertyInfo, check for bad strings, write your query comparison function, etc., but at its heart, this is what you do.


You can use dynamic Linq - check out this blog.

Also check out this StackOverFlow post...


More productive than reflection extension to dynamic order items:

public static class DynamicExtentions
{
    public static object GetPropertyDynamic<Tobj>(this Tobj self, string propertyName) where Tobj : class
    {
        var param = Expression.Parameter(typeof(Tobj), "value");
        var getter = Expression.Property(param, propertyName);
        var boxer = Expression.TypeAs(getter, typeof(object));
        var getPropValue = Expression.Lambda<Func<Tobj, object>>(boxer, param).Compile();            
        return getPropValue(self);
    }
}

Example:

var ordered = items.OrderBy(x => x.GetPropertyDynamic("ProductId"));

Also you may need to cache complied lambas(e.g. in Dictionary<>)


Also Dynamic Expressions can solve this problem. You can use string-based queries through LINQ expressions that could have been dynamically constructed at run-time.

var query = query
          .Where("Category.CategoryName == @0 and Orders.Count >= @1", "Book", 10)
          .OrderBy("ProductId")
          .Select("new(ProductName as Name, Price)");

참고URL : https://stackoverflow.com/questions/1689199/c-sharp-code-to-order-by-a-property-using-the-property-name-as-a-string

반응형