문제

에서 예를 찾았습니다. VS2008 예 SQL과 유사한 문자열(예: OrderBy("Name, Age DESC")) 주문을 위해.불행하게도 포함된 방법은 다음에서만 작동합니다. IQueryable<T>;.이 기능을 활성화할 수 있는 방법이 있나요? IEnumerable<T>?

도움이 되었습니까?

해결책

방금 이 오래된 것을 우연히 발견했습니다...

동적 LINQ 라이브러리 없이 이 작업을 수행하려면 아래와 같은 코드만 있으면 됩니다.여기에는 중첩 속성을 포함한 가장 일반적인 시나리오가 포함됩니다.

그것을 작동시키려면 IEnumerable<T> 다음을 통해 이동하는 래퍼 메서드를 추가할 수 있습니다. AsQueryable -하지만 아래 코드가 핵심입니다 Expression 논리가 필요합니다.

public static IOrderedQueryable<T> OrderBy<T>(
    this IQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "OrderBy");
}

public static IOrderedQueryable<T> OrderByDescending<T>(
    this IQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "OrderByDescending");
}

public static IOrderedQueryable<T> ThenBy<T>(
    this IOrderedQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "ThenBy");
}

public static IOrderedQueryable<T> ThenByDescending<T>(
    this IOrderedQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "ThenByDescending");
}

static IOrderedQueryable<T> ApplyOrder<T>(
    IQueryable<T> source, 
    string property, 
    string methodName) 
{
    string[] props = property.Split('.');
    Type type = typeof(T);
    ParameterExpression arg = Expression.Parameter(type, "x");
    Expression expr = arg;
    foreach(string prop in props) {
        // use reflection (not ComponentModel) to mirror LINQ
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
    Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
    LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);

    object result = typeof(Queryable).GetMethods().Single(
            method => method.Name == methodName
                    && method.IsGenericMethodDefinition
                    && method.GetGenericArguments().Length == 2
                    && method.GetParameters().Length == 2)
            .MakeGenericMethod(typeof(T), type)
            .Invoke(null, new object[] {source, lambda});
    return (IOrderedQueryable<T>)result;
}

편집하다:그걸 섞으면 더 재미있어지죠 dynamic - 그래도 참고하세요 dynamic LINQ-to-Object에만 적용됩니다(ORM 등의 표현식 트리는 실제로 표현할 수 없음). dynamic 쿼리 - MemberExpression 지원하지 않습니다).하지만 LINQ-to-Objects를 사용하여 이를 수행하는 방법이 있습니다.참고로 선택은 Hashtable 유리한 잠금 의미로 인해 발생합니다.

using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Runtime.CompilerServices;
static class Program
{
    private static class AccessorCache
    {
        private static readonly Hashtable accessors = new Hashtable();

        private static readonly Hashtable callSites = new Hashtable();

        private static CallSite<Func<CallSite, object, object>> GetCallSiteLocked(
            string name) 
        {
            var callSite = (CallSite<Func<CallSite, object, object>>)callSites[name];
            if(callSite == null)
            {
                callSites[name] = callSite = CallSite<Func<CallSite, object, object>>
                    .Create(Binder.GetMember(
                                CSharpBinderFlags.None, 
                                name, 
                                typeof(AccessorCache),
                                new CSharpArgumentInfo[] { 
                                    CSharpArgumentInfo.Create(
                                        CSharpArgumentInfoFlags.None, 
                                        null) 
                                }));
            }
            return callSite;
        }

        internal static Func<dynamic,object> GetAccessor(string name)
        {
            Func<dynamic, object> accessor = (Func<dynamic, object>)accessors[name];
            if (accessor == null)
            {
                lock (accessors )
                {
                    accessor = (Func<dynamic, object>)accessors[name];
                    if (accessor == null)
                    {
                        if(name.IndexOf('.') >= 0) {
                            string[] props = name.Split('.');
                            CallSite<Func<CallSite, object, object>>[] arr 
                                = Array.ConvertAll(props, GetCallSiteLocked);
                            accessor = target =>
                            {
                                object val = (object)target;
                                for (int i = 0; i < arr.Length; i++)
                                {
                                    var cs = arr[i];
                                    val = cs.Target(cs, val);
                                }
                                return val;
                            };
                        } else {
                            var callSite = GetCallSiteLocked(name);
                            accessor = target =>
                            {
                                return callSite.Target(callSite, (object)target);
                            };
                        }
                        accessors[name] = accessor;
                    }
                }
            }
            return accessor;
        }
    }

    public static IOrderedEnumerable<dynamic> OrderBy(
        this IEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.OrderBy<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    public static IOrderedEnumerable<dynamic> OrderByDescending(
        this IEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.OrderByDescending<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    public static IOrderedEnumerable<dynamic> ThenBy(
        this IOrderedEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.ThenBy<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    public static IOrderedEnumerable<dynamic> ThenByDescending(
        this IOrderedEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.ThenByDescending<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    static void Main()
    {
        dynamic a = new ExpandoObject(), 
                b = new ExpandoObject(), 
                c = new ExpandoObject();
        a.X = "abc";
        b.X = "ghi";
        c.X = "def";
        dynamic[] data = new[] { 
            new { Y = a },
            new { Y = b }, 
            new { Y = c } 
        };

        var ordered = data.OrderByDescending("Y.X").ToArray();
        foreach (var obj in ordered)
        {
            Console.WriteLine(obj.Y.X);
        }
    }
}

다른 팁

복잡하지 않고 너무 쉽습니다.

  1. 추가하다 using System.Linq.Dynamic; 상단에.
  2. 사용 vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

나는 답을 찾았다.나는 사용할 수 있습니다 .AsQueryable<>() 내 목록을 IQueryable로 변환한 다음 이에 대해 동적 순서를 실행하는 확장 메서드입니다.

이 질문을 우연히 발견했습니다.

위에서 Marc의 ApplyOrder 구현을 사용하여 다음과 같은 SQL과 유사한 문자열을 처리하는 Extension 메서드를 함께 사용했습니다.

list.OrderBy("MyProperty DESC, MyOtherProperty ASC");

자세한 내용은 여기에서 확인할 수 있습니다. http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html

정렬하려는 속성을 얻으려면 리플렉션을 사용하는 것이 효과적일 것 같습니다.

IEnumerable<T> myEnumerables
var query=from enumerable in myenumerables
          where some criteria
          orderby GetPropertyValue(enumerable,"SomeProperty")
          select enumerable

private static object GetPropertyValue(object obj, string property)
{
    System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property);
    return propertyInfo.GetValue(obj, null);
}

리플렉션을 사용하는 것은 속성에 직접 액세스하는 것보다 상당히 느리므로 성능을 조사해야 합니다.

다른 사람들이 말한 것을 토대로 구축하십시오.나는 다음이 꽤 잘 작동한다는 것을 알았습니다.

public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> input, string queryString)
{
    if (string.IsNullOrEmpty(queryString))
        return input;

    int i = 0;
    foreach (string propname in queryString.Split(','))
    {
        var subContent = propname.Split('|');
        if (Convert.ToInt32(subContent[1].Trim()) == 0)
        {
            if (i == 0)
                input = input.OrderBy(x => GetPropertyValue(x, subContent[0].Trim()));
            else
                input = ((IOrderedEnumerable<T>)input).ThenBy(x => GetPropertyValue(x, subContent[0].Trim()));
        }
        else
        {
            if (i == 0)
                input = input.OrderByDescending(x => GetPropertyValue(x, subContent[0].Trim()));
            else
                input = ((IOrderedEnumerable<T>)input).ThenByDescending(x => GetPropertyValue(x, subContent[0].Trim()));
        }
        i++;
    }

    return input;
}

나는이 질문을 우연히 발견했다.

이를 수행하는 방법은 다음과 같습니다.

var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age);    

이 작업을 수행하려고 했지만 문제가 발생했습니다. Kjetil Watnedal의 솔루션 나는 인라인 linq 구문을 사용하지 않기 때문에 메소드 스타일 구문을 선호합니다.내 특정 문제는 사용자 정의를 사용하여 동적 정렬을 수행하는 것이었습니다. IComparer.

내 솔루션은 다음과 같이 끝났습니다.

다음과 같은 IQueryable 쿼리가 제공됩니다.

List<DATA__Security__Team> teams = TeamManager.GetTeams();
var query = teams.Where(team => team.ID < 10).AsQueryable();

런타임 정렬 필드 인수가 주어지면 다음과 같습니다.

string SortField; // Set at run-time to "Name"

동적 OrderBy는 다음과 같습니다.

query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField));

그리고 GetReflectedPropertyValue()라는 작은 도우미 메서드를 사용하고 있습니다.

public static string GetReflectedPropertyValue(this object subject, string field)
{
    object reflectedValue = subject.GetType().GetProperty(field).GetValue(subject, null);
    return reflectedValue != null ? reflectedValue.ToString() : "";
}

마지막으로 한 가지 - 제가 원한다고 말씀드렸는데요. OrderBy 사용자 정의를 사용하려면 IComparer - 하고 싶었기 때문에 자연적인 분류.

그렇게 하려면 다음을 변경하면 됩니다. OrderBy 에게:

query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField), new NaturalSortComparer<string>());

보다 이 게시물 코드에 대한 NaturalSortComparer().

다음과 같이 추가할 수 있습니다.

public static IEnumerable<T> OrderBy( this IEnumerable<T> input, string queryString) {
    //parse the string into property names
    //Use reflection to get and sort by properties
    //something like

    foreach( string propname in queryString.Split(','))
        input.OrderBy( x => GetPropertyValue( x, propname ) );

    // I used Kjetil Watnedal's reflection example
}

그만큼 GetPropertyValue 기능은 다음과 같습니다 Kjetil Watnedal의 답변

문제는 왜일까요?이러한 정렬은 D2VIANT의 답변과 같이 컴파일 타임이 아닌 런타임에 예외를 발생시킵니다.

Linq to Sql을 처리하고 orderby가 표현식 트리인 경우 어쨌든 실행을 위해 SQL로 변환됩니다.

제가 흥미로운 것을 발견한 또 다른 것이 있습니다.소스가 DataTable인 경우 Dynamic Linq를 사용하지 않고도 동적 정렬을 사용할 수 있습니다.

DataTable orders = dataSet.Tables["SalesOrderHeader"];
EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()
                                         orderby order.Field<DateTime>("OrderDate")
                                         select order;
DataView view = query.AsDataView();
bindingSource1.DataSource = view;

참조: http://msdn.microsoft.com/en-us/library/bb669083.aspx (DataSetExtensions 사용)

DataView로 변환하여 이를 수행하는 또 다른 방법은 다음과 같습니다.

DataTable contacts = dataSet.Tables["Contact"];    
DataView view = contacts.AsDataView();    
view.Sort = "LastName desc, FirstName asc";    
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

Maarten에게 감사드립니다(LINQ에서 PropertyInfo 개체를 사용하여 컬렉션 쿼리) 나는 다음과 같은 해결책을 얻었습니다.

myList.OrderByDescending(x => myPropertyInfo.GetValue(x, null)).ToList();

내 경우에는 "ColumnHeaderMouseClick"(WindowsForm)을 작업 중이었기 때문에 특정 열이 눌려졌고 해당 PropertyInfo가 발견되었습니다.

foreach (PropertyInfo column in (new Process()).GetType().GetProperties())
{
    if (column.Name == dgvProcessList.Columns[e.ColumnIndex].Name)
    {}
}

또는

PropertyInfo column = (new Process()).GetType().GetProperties().Where(x => x.Name == dgvProcessList.Columns[e.ColumnIndex].Name).First();

(열 이름이 개체 속성과 일치하는지 확인하십시오)

건배

많은 검색 끝에 이것이 나에게 도움이 되었습니다.

public static IEnumerable<TEntity> OrderBy<TEntity>(this IEnumerable<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, property.PropertyType },
                                           source.AsQueryable().Expression, 
                                           Expression.Quote(orderByExpression));
    return source.AsQueryable().Provider.CreateQuery<TEntity>(resultExpression);
}

IEnumerable을 IQueryable로 변환할 수 있습니다.

items = items.AsQueryable().OrderBy("Name ASC");

대체 솔루션은 다음 클래스/인터페이스를 사용합니다.실제로 역동적이지는 않지만 작동합니다.

public interface IID
{
    int ID
    {
        get; set;
    }
}

public static class Utils
{
    public static int GetID<T>(ObjectQuery<T> items) where T:EntityObject, IID
    {
        if (items.Count() == 0) return 1;
        return items.OrderByDescending(u => u.ID).FirstOrDefault().ID + 1;
    }
}

이 답변은 에서 제공하는 솔루션에 대한 예가 필요한 의견에 대한 응답입니다. @John Sheehan - Runscope

우리 모두에게 모범을 보여주세요.

DAL(데이터 액세스 계층)에서,

IEnumerable 버전:

  public  IEnumerable<Order> GetOrders()
    {
      // i use Dapper to return IEnumerable<T> using Query<T>
      //.. do stuff
      return  orders  // IEnumerable<Order>
  }

IQueryable 버전

  public IQueryable<Order> GetOrdersAsQuerable()
    {
        IEnumerable<Order> qry= GetOrders();
        //use the built-in extension method  AsQueryable in  System.Linq namespace
        return qry.AsQueryable();            
    }

이제 IQueryable 버전을 사용하여 바인딩할 수 있습니다(예: Asp.net의 GridView 및 정렬 이점)(IEnumerable 버전을 사용하여 정렬할 수 없음)

나는 Dapper를 ORM으로 사용하고 IQueryable 버전을 구축했으며 asp.net의 GridView에서 정렬을 매우 쉽게 활용했습니다.

먼저 동적 설치도구 --> NuGet 패키지 관리자 --> 패키지 관리자 콘솔

install-package System.Linq.Dynamic

추가하다 네임스페이스 using System.Linq.Dynamic;

이제 다음을 사용할 수 있습니다. OrderBy("Name, Age DESC")

목록을 IEnumerable 또는 Iquerable로 변환하고 System.LINQ.Dynamic 네임스페이스를 사용하여 추가한 다음 기본적으로 System.LINQ.Dynamic에서 제공되는 OrderBy 메서드에 쉼표로 구분된 문자열로 속성 이름을 언급할 수 있습니다.

동적 사용 linq

그냥 추가해 using System.Linq.Dynamic;

모든 열의 순서를 지정하려면 다음과 같이 사용하세요.

string sortTypeStr = "ASC"; // or DESC
string SortColumnName = "Age"; // Your column name
query = query.OrderBy($"{SortColumnName} {sortTypeStr}");
var result1 = lst.OrderBy(a=>a.Name);// for ascending order. 
 var result1 = lst.OrderByDescending(a=>a.Name);// for desc order. 
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top