Frage

Ein Beispiel habe ich im gefunden VS2008-Beispiele für Dynamic LINQ, mit dem Sie eine SQL-ähnliche Zeichenfolge verwenden können (z. B. OrderBy("Name, Age DESC")) zum Bestellen.Leider funktioniert die enthaltene Methode nur auf IQueryable<T>;.Gibt es eine Möglichkeit, diese Funktionalität zu aktivieren? IEnumerable<T>?

War es hilfreich?

Lösung

Bin gerade über diesen Oldie gestolpert ...

Um dies ohne die dynamische LINQ-Bibliothek zu tun, benötigen Sie lediglich den folgenden Code.Dies deckt die gängigsten Szenarien ab, einschließlich verschachtelter Eigenschaften.

Damit es funktioniert IEnumerable<T> Sie könnten einige Wrapper-Methoden hinzufügen, die über ausgeführt werden AsQueryable - aber der Code unten ist der Kern Expression Logik nötig.

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

Bearbeiten:Es macht mehr Spaß, wenn man das damit mischen möchte dynamic - obwohl beachten Sie das dynamic Gilt nur für LINQ-to-Objects (Ausdrucksbäume für ORMs usw. können nicht wirklich dargestellt werden dynamic Anfragen - MemberExpression unterstützt es nicht).Aber hier ist eine Möglichkeit, dies mit LINQ-to-Objects zu tun.Beachten Sie, dass die Auswahl von Hashtable liegt an der günstigen Sperrsemantik:

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

Andere Tipps

Zu einfach und ohne Komplikationen:

  1. Hinzufügen using System.Linq.Dynamic; oben.
  2. Verwenden vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

Ich habe die Antwort gefunden.Ich kann das verwenden .AsQueryable<>() Erweiterungsmethode, um meine Liste in IQueryable zu konvertieren, und führe dann die dynamische Reihenfolge dagegen aus.

Bin gerade über diese Frage gestolpert.

Unter Verwendung der ApplyOrder-Implementierung von Marc von oben habe ich eine Erweiterungsmethode zusammengestellt, die SQL-ähnliche Zeichenfolgen verarbeitet wie:

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

Einzelheiten finden Sie hier: http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html

Ich denke, es würde funktionieren, Reflektion zu verwenden, um die Eigenschaft zu erhalten, nach der Sie sortieren möchten:

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

Beachten Sie, dass die Verwendung von Reflektion erheblich langsamer ist als der direkte Zugriff auf die Eigenschaft, daher müsste die Leistung untersucht werden.

Ich baue einfach auf dem auf, was andere gesagt haben.Ich habe festgestellt, dass Folgendes ganz gut funktioniert.

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

Ich stolpere diese Frage auf der Suche nach linq mehrere Orderby -Klauseln und vielleicht war es das, wonach der Autor suchte

So geht's:

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

Ich habe versucht, dies zu tun, hatte aber Probleme damit Kjetil Watnedals Lösung Da ich nicht die Inline-Linq-Syntax verwende, bevorzuge ich die Syntax im Methodenstil.Mein spezifisches Problem bestand darin, eine dynamische Sortierung mithilfe einer benutzerdefinierten Methode durchzuführen IComparer.

Meine Lösung endete so:

Gegeben sei eine IQueryable-Abfrage wie folgt:

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

Und wenn ein Sortierfeldargument zur Laufzeit gegeben ist:

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

Das dynamische OrderBy sieht folgendermaßen aus:

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

Und das mithilfe einer kleinen Hilfsmethode namens GetReflectedPropertyValue():

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

Eine letzte Sache: Ich habe erwähnt, dass ich das wollte OrderBy benutzerdefiniert verwenden IComparer - weil ich es tun wollte Natürliche Sortierung.

Dazu ändere ich einfach die OrderBy Zu:

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

Sehen dieser Beitrag für den Code für NaturalSortComparer().

Sie könnten es hinzufügen:

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
}

Der GetPropertyValue Funktion ist von Kjetil Watnedals Antwort

Die Frage wäre: Warum?Eine solche Sortierung würde Ausnahmen zur Laufzeit und nicht zur Kompilierungszeit auslösen (wie die Antwort von D2VIANT).

Wenn Sie mit Linq to Sql arbeiten und orderby ein Ausdrucksbaum ist, wird er zur Ausführung trotzdem in SQL konvertiert.

Hier ist noch etwas, das ich interessant fand.Wenn Ihre Quelle eine DataTable ist, können Sie die dynamische Sortierung verwenden, ohne Dynamic Linq zu verwenden

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;

Referenz: http://msdn.microsoft.com/en-us/library/bb669083.aspx (Verwenden von DataSetExtensions)

Hier ist eine weitere Möglichkeit, dies zu tun, indem Sie es in eine DataView konvertieren:

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

Danke an Maarten (Fragen Sie eine Sammlung mithilfe des PropertyInfo-Objekts in LINQ ab) Ich habe diese Lösung:

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

In meinem Fall habe ich an einem „ColumnHeaderMouseClick“ (WindowsForm) gearbeitet, also habe ich gerade die spezifische gedrückte Spalte und die entsprechende PropertyInfo gefunden:

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

ODER

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

(Stellen Sie sicher, dass Ihre Spaltennamen mit den Objekteigenschaften übereinstimmen.)

Prost

Nach langem Suchen hat das bei mir funktioniert:

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

Sie können IEnumerable in IQueryable konvertieren.

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

Eine alternative Lösung verwendet die folgende Klasse/Schnittstelle.Es ist nicht wirklich dynamisch, aber es funktioniert.

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

Diese Antwort ist eine Antwort auf die Kommentare, die ein Beispiel für die von bereitgestellte Lösung benötigen @John Sheehan – Runscope

Bitte geben Sie ein Beispiel für den Rest von uns.

in DAL (Data Access Layer),

Die IEnumerable-Version:

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

Die IQueryable-Version

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

Jetzt können Sie die IQueryable-Version zum Binden verwenden, beispielsweise GridView in Asp.net, und von der Sortierung profitieren (Sie können nicht mit der IEnumerable-Version sortieren).

Ich habe Dapper als ORM verwendet und eine IQueryable-Version erstellt und die Sortierung in GridView in asp.net ganz einfach genutzt.

Installieren Sie zunächst DynamicExtras -> NuGet-Paketmanager -> Paket-Manager-Konsole

install-package System.Linq.Dynamic

Hinzufügen Namensraum using System.Linq.Dynamic;

Jetzt können Sie es verwenden OrderBy("Name, Age DESC")

Konvertieren Sie die Liste in IEnumerable oder Iquerable, fügen Sie sie mithilfe des System.LINQ.Dynamic-Namespace hinzu. Anschließend können Sie die Eigenschaftsnamen in einer durch Kommas getrennten Zeichenfolge in der OrderBy-Methode angeben, die standardmäßig aus System.LINQ.Dynamic stammt.

Verwenden Sie Dynamik linq

einfach hinzufügen using System.Linq.Dynamic;

Und verwenden Sie es so, um alle Ihre Spalten zu ordnen:

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. 
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top