4 Stimmen

c# - Bestellung einer Linq-Abfrage

Für mein allgemeines Gitter tue ich dies derzeit, um die Sortierung zu aktivieren:

Elements.OrderBy(column.SortExpression).AsQueryable();

In dem SortExpression ist vom Typ Func<T, object> und Spalte ist eine generische Klasse Column<T>

Ich setze SortExpression in einem Controller wie folgt:

new Column<OrderViewData>{Key = "ShippingDate", SortExpression = e => e.ShippingDate}

Das "OrderBy" führt zu einer Ausführung der SQL-Anweisung, was ich nicht möchte.

Ich versuche also, sie durch diese zu ersetzen:

Elements = from element in Elements
               orderby column.SortExpression
               select element;

Dadurch wird die Sql-Ausführung nicht ausgelöst.

Nun, natürlich Spalte SortExpression sollte von einem anderen Typ sein. Nur ich kann nicht wirklich herausfinden, welche Art es sein sollte und wie man es auf die generische Klasse in der Steuerung festgelegt.

Ich sollte immer noch in der Lage sein, die SortExpression in einer generischen, stark typisierten Art und Weise einer Art.

Irgendwelche Vorschläge, wie ich durch einen Ausdruck irgendwo anders in der Anwendung festgelegt bestellen kann, ohne die Sql beim Anwenden der Reihenfolge auf die IQueryable ausführen?

@ Earwicker:

Das funktioniert:

Expression<Func<Employee, DateTime?>> sortexpression = e => e.BirthDate;

var db = new NorthwindDataContext();
var query = from e in db.Employees
                select e;
query = query.OrderBy(sortexpression);

int count = query.Count();

Und erzeugt:

SELECT COUNT(*) AS [value]
FROM [dbo].[Employees] AS [t0]

Wenn ich die DateTime ? in der ersten Zeile mit Objekt:

Expression<Func<Employee, object>> sortexpression = e => e.BirthDate;

Ich erhalte diese Ausnahme:

InvalidOperationException: Bestellung nach Typ 'System.Object' nicht möglich

Nun könnte man sagen: "Dann benutze doch einfach DateTime ?", aber ich möchte, dass der Aufbau der Spalten in meinem generischen Gitter so wenig Code wie möglich erfordert. Ich möchte nicht, dass die Leute den ganzen Code eingeben müssen. Expression<Func<Employee, some_type>> . Ich möchte, dass die Leute einfach etwas Kleines wie meinen ersten Versuch tippen können SortExpression = e => e.BirthDate , wo ich die Vorteile der generischen Klasse Column nutze, um 'T' zu definieren.

Glauben Sie, dass es möglich wäre, eine Art Erweiterung zu erstellen, die irgendwie den Typ der e.BirthDate und wirft dann die Func<T, object> a Expression<Func<T,some_type>> ? Dann könnte ich im internen Code etwas tun wie: Elements.OrderBy(column.SortExpression.FixCast())

Dass mein interner Code hässlich oder komplex ist, ist mir im Moment ziemlich egal. Ich muss die SQL-Abfragen richtig hinbekommen und mich um die Benutzerfreundlichkeit für Entwickler kümmern, die das Raster verwenden.

Vielen Dank, dass Sie mir geholfen haben!

@ earwicker 2:

var gridBuilder = new RainbowGridBuilder<Employee> ("Examples_Employees") 
{

    Elements = GetEmployees(),
    //The elements (records) we will show in our grid. 

    //Define the columns for our grid.
    Columns = new List<Column<Employee >> 
    {
        new Column<Employee> 
        {
            Key = "EmployeeId", 
            //Key is used for sorting, selecting columns, ...

            Header = "Employee Id", 
            //The header of the column. Also used as caption in the column selection checkboxlist.

            ValueExpression = e => e.EmployeeID.ToString(), 

            //The Linq expression which will be executed on each element to fill the cell

            SortExpression = e => e.EmployeeID, 
            //The Linq expression by which to sort the elements in the grid. 

            Display = false
        }, //Is this column visible by default?

        new Column<Employee> 
        {
            Key = "Name",
            ValueExpression = e => 
                e.FirstName + " " + e.LastName,
            SortExpression = e => e.LastName
      } ,
    },

    // Some other properties here that are irrelevant.
}

3voto

Daniel Earwicker Punkte 111630

SortExpression sollte vom Typ Expression<Func<T, object>> .

Indem sie es zu einem Func<T, object> wird der Compiler veranlasst, sie direkt in ausführbare AWL zu reduzieren. Linq to SQL (oder Entities, oder NHibernate, oder was auch immer) benötigt den Code in Form eines Ausdrucksbaums, um ihn in SQL oder eine andere Abfragesprache zur Ausführung an anderer Stelle übersetzen zu können. Durch Zuweisung Ihres Lambdas an Expression<Func<...>> lösen Sie die Kompilierung zu einem Ausdrucksbaum aus.

Funktioniert es, wenn Sie das hier einsetzen?

Elements = Elements.OrderBy(e => e.ShippingDate);

Und was ist damit?

Expression<Func<OrderViewData, object>> sortExpression = e => e.ShippingDate;
Elements = Elements.OrderBy(sortExpression);

Ausgehend von Ihrer aktualisierten Frage klingt es so, als müssten Sie einen Ausdruck mit einem Rückgabewert vom Typ statisch erfassen, anstatt object .

Es ist schwer zu sagen, was für Sie die ideale Anordnung wäre, aber in Ihrem ursprünglichen Einrichtungscode hatten Sie:

new Column<OrderViewData>{Key = "ShippingDate", SortExpression = e => e.ShippingDate}

Angenommen, Column zwei Typ-Parameter angenommen, TElem (die Art des in der Spalte gespeicherten Wertes) und TSort (der Typ des Wertes, nach dem sortiert werden soll).

new Column<Employee, DateTime?> { SortExpression = e => e.BirthDate }

Das sieht für mich nicht zu sperrig aus, und SortExpression würde dann einfach sein:

Expression<Func<TElem, TSort>> SortExpression { get; set; }

1voto

Thomas Stock Punkte 10649

Ich habe eine Lösung gefunden, mit der ich das erreichen konnte, was ich wollte.

Verwendung: Einstellung von stark typisierten Sortexpressionen für Spalten:

Columns = new List<Column<Employee>>{
            new Column<Employee> {
                Key = "EmployeeId",   
                SortExpression = new SortExpression<Employee>(e => e.EmployeeID)
                // ... other irrelevant column properties ...  
            },
            new Column<Employee> {
                Key = "EmployeeBirthDate",      
                SortExpression = new SortExpression<Employee>(e => e.BirthDate)
            }
          };

Zugrunde liegender Code

Diese wenigen Zeilen in der Methode "buildGrid()" in der Klasse RainbowGridBuilder wenden die Sortierung an:

if (columnToSort != null) //sort the elements according to the set column key and the sortexpression
{
    var column = Columns.Where(c => c.Key == columnToSort).SingleOrDefault();
    if (column != null)
    {
        Elements = column.SortExpression.ApplySortExpression(Elements, descending);
    }
}

Wie es funktioniert:

Durch die Verwendung von überladenen Konstruktoren in der Klasse SortExpression, die ich erstellt habe, kann ich eine private Expression<Func<T, some_specific_Type>> .

Innerhalb der Klasse SortExpression gibt es eine Methode ApplySortExpression, die nach einem privaten Member sucht, der nicht null ist, und entsprechend sortiert:

public class SortExpression<T>
{
    private Expression<Func<T, DateTime?>> DateTimeExpression { get; set; }
    private Expression<Func<T, int?>> intExpression { get; set; }
    private Expression<Func<T, string>> stringExpression { get; set; }

    public SortExpression(Expression<Func<T, DateTime?>> expression)
    {
        DateTimeExpression = expression;
    }

    public SortExpression(Expression<Func<T, int?>> expression)
    {
        intExpression = expression;
    }

    public SortExpression(Expression<Func<T, string>> expression)
    {
        stringExpression = expression;
    }

    public IQueryable<T> ApplySortExpression(IQueryable<T> elements, bool? descending)
    {
        if (DateTimeExpression != null)
        {
            if (descending.HasValue && descending.Value)
                return elements.OrderByDescending(DateTimeExpression);
            else
                return elements.OrderBy(DateTimeExpression);
        }
        else if (intExpression != null)
        {
            if (descending.HasValue && descending.Value)
                return elements.OrderByDescending(intExpression);
            else
                return elements.OrderBy(intExpression);
        }
        else if (stringExpression != null)
        {
            if (descending.HasValue && descending.Value)
                return elements.OrderByDescending(stringExpression);
            else
                return elements.OrderBy(stringExpression);
        }
        else
            throw new Exception("Unsuported sortkey type");
    }
}

Diese Lösung ist zwar unter der Haube nicht ganz sauber, aber die Benutzerfreundlichkeit ist das, was ich gesucht habe. Die einzige Änderung am verbrauchenden Code war das Hinzufügen von " new SortExpression<Employee> ".

0voto

jrista Punkte 31522

Ich denke, die Wurzel des Problems liegt in der Tatsache, dass Sie OrderBy vor Konvertierung in ein Queryable. Wenn Sie auf einem IEnumerable<T> bestellen, brauchen Sie zuerst etwas zum Bestellen. Im Gegensatz zu IQueryable<T> baut IEnumerable<T> keine Ausdrucksbäume auf... es wickelt ein Enumerable um das andere. Um die Vorteile der verzögerten Ausführung zu nutzen, müssen Sie sicherstellen, dass Sie von Anfang an mit einer IQueryable arbeiten. Eine Konvertierung in eine IQueryable, nachdem Sie alle Ihre Filterung, Sortierung usw. durchgeführt haben, bringt keine Vorteile mehr.

0voto

Hier ist meine einfache Lösung für dieses Orderby-Problem:

        var SampleData = new SampleDataContext();

        var text = from i in SampleData.Events
                   select i;
        var text2 = text.OrderByDescending(i => i.id);
        return View(text2);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X