C # LINQ to SQL: Refactoring dieser generischen GetByID-Methode

c# expression-trees generics linq-to-sql

Frage

Ich habe die folgende Methode geschrieben.

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

Grundsätzlich ist es eine Methode in einer generischen Klasse, wobei T eine Klasse in einem DataContext ist.

Die Methode GetTable die Tabelle vom Typ T ( GetTable ) ab und GetTable nach der ersten Eigenschaft (die immer die ID ist) nach dem eingegebenen Parameter.

Das Problem dabei ist, dass ich zuerst die Tabelle der Elemente in eine Liste konvertieren musste, um einen GetType für die Eigenschaft auszuführen, aber das ist nicht sehr praktisch, weil alle Elemente der Tabelle aufgelistet und in eine List konvertiert werden müssen.

Wie kann ich diese Methode umgestalten, um eine ToList für die gesamte Tabelle zu vermeiden?

[Aktualisieren]

Der Grund, warum ich das Where direkt auf dem Tisch nicht ausführen kann, ist, weil ich diese Ausnahme erhalte:

Methode 'System.Reflection.PropertyInfo [] GetProperties ()' hat keine unterstützte Übersetzung in SQL.

GetProperties kann nicht in SQL übersetzt werden.

[Aktualisieren]

Einige Leute haben vorgeschlagen, eine Schnittstelle für T zu verwenden , aber das Problem ist, dass der T Parameter eine Klasse ist, die automatisch in [DataContextName] .designer.cs generiert wird , und daher kann ich keine Schnittstelle implementieren (und es ist nicht machbar, sie zu implementieren) die Schnittstellen für alle diese "Datenbankklassen" von LINQ; außerdem wird die Datei neu generiert, sobald ich dem DataContext neue Tabellen hinzufüge, wodurch alle geschriebenen Daten verloren gehen.

Also, es muss einen besseren Weg geben, dies zu tun ...

[Aktualisieren]

Ich habe jetzt meinen Code implementiert, wie Neil Williams 'Vorschlag, aber ich habe immer noch Probleme. Hier sind Auszüge aus dem Code:

Schnittstelle:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

DataContext [Code anzeigen]:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

Generische Methode:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

Die Ausnahme wird auf diese Zeile geworfen: return table.SingleOrDefault(e => e.ID.Equals(id)); und die Ausnahme ist:

System.NotSupportedException: The member 'MusicRepo_DataContext.IHasID.ID' has no supported translation to SQL.

[Update] Lösung:

Mit der Hilfe von Denis Trollers geschriebener Antwort und dem Link zum Beitrag im Code Rant Blog habe ich endlich eine Lösung gefunden:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

Akzeptierte Antwort

Sie müssen einen Ausdrucksbaum erstellen, den LINQ to SQL verstehen kann. Angenommen, Ihre "id" -Eigenschaft heißt immer "id":

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

Dies sollte den Trick machen. Es wurde schamlos von diesem Blog ausgeliehen . Das ist im Grunde, was LINQ to SQL macht, wenn Sie eine Abfrage schreiben

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

Sie machen nur die Arbeit für LTS, weil der Compiler das für Sie nicht erstellen kann, da nichts erzwingen kann, dass T eine "ID" -Eigenschaft hat, und Sie keine willkürliche "ID" -Eigenschaft von einer Schnittstelle zur Datenbank zuordnen können.

==== UPDATE ====

OK, hier ist eine einfache Implementierung, um den Namen des Primärschlüssels zu finden, vorausgesetzt, es gibt nur einen (keinen zusammengesetzten Primärschlüssel) und angenommen, dass alles gut typisiert ist (dh Ihr Primärschlüssel ist mit dem "Kurz" -Typ kompatibel) Verwendung in der GetById-Funktion):

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

Beliebte Antwort

Einige Gedanken...

Entfernen Sie einfach den ToList () -Aufruf, SingleOrDefault arbeitet mit einem IEnumerably, die ich vermute Tabelle ist.

Zwischenspeichern des Aufrufs von e.GetType (). GetProperties (). First (), um die PropertyInfo zurückzugeben.

Können Sie nicht einfach eine Einschränkung zu T hinzufügen, die sie zwingt, eine Schnittstelle zu implementieren, die die Id-Eigenschaft freilegt?




Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum