2016-06-03 34 views
11

Ich habe eine abstrakte Klasse, die von zwei Klassen geerbt wird. Beide Klassen repräsentieren Tabellen in der Datenbank. Ich habe Probleme, Ausdrücke in der abstrakten Klasse zuzuordnen und deshalb erhalte ich Ausnahmen, die nicht in SQL übersetzt werden können. Alle Fragen, die ich in Stackoverflow gefunden habe, sprechen über Spalten, die bereits für mich arbeiten.Mapping Ausdrücke in LINQ-to-sql abstrakte Klasse

Unten ist ein sehr einfacher Code, der zeigt, was ich meine. Auto und Motorrad haben vollständig getrennte Implementierungen von _isNew Expression.

public abstract class Vehicle { 
    public abstract boolean IsNew; 
} 

public partial class Car: Vehicle { 
     public override boolean IsNew { 
     get { _isNew.Invoke(this); } 
     } 
} 
public partial class Motorcycle: Vehicle { 
     public override boolean IsNew { 
     get { _isNew.Invoke(this); } 
     } 
} 

Ich möchte in der Lage sein, entweder _isNew Ausdruck oder IsNew Eigenschaft auf einem IQueryable zu nennen, und doch läuft es die _isNew der Car-Klasse bei Fahrzeugtyp des Autos ist. Kann ich das überhaupt schaffen? Alle von mir getesteten Lösungen haben eine Ausnahme verursacht, die nicht in SQL übersetzt werden kann.

+0

Was ist '_isNew'? Kann nicht ein Ausdruck sein, der * übersetzt werden kann? –

+0

_isNew ist ein Ausdruck, der in SQL übersetzt werden kann (zB für Autos Ausdruck > _isNew = (v) => v.Age <2; für Motorräder Ausdruck > _isNew = (v) => v.Age <1; (PS: Alter ist eine DB-Spalte) Mein Hauptproblem ist, wie kann ich das richtige _isNew auswählen, das auf Vehicle aufgerufen werden soll, abhängig von der geerbten Klasse Die Funktion IsNew wird einen SQL-Übersetzungsfehler verursachen und ich kann nicht entscheiden, welche _IsNew aufrufen soll, ohne den Typ und das Casting jedes Mal zu überprüfen. – Sami

+0

Ich denke, das wird überhaupt nicht funktionieren, denn um Ihre Überschreibung von IsNew aufzurufen, wird eine konkrete Instanz von Auto oder Motorrad notwendig sein. Daher müsste EF zuerst die Elemente abfragen, um diese Instanzen zu erstellen. – Peit

Antwort

1

Bevor ich auf Ihre Frage komme, sollten Sie wahrscheinlich auf die best practices of C# properties concerning exceptions überprüfen.

Sie könnten nur Ihre Liste eifrig laden, und rufen Sie danach Ihre IsNew-Eigenschaft, die den Linq-to-SQL-Fehler beseitigt. Aber ich verstehe, dass dies zu Leistungsproblemen führen kann, wenn Sie sich darauf verlassen, dass IsNew nach einer großen Menge von Daten in diesem Filter sucht.

Ich denke, Ihr Problem liegt wirklich daran, dass Sie eine Instanz eines Fahrzeugs verwenden möchten, um die Eigenschaft "IsNew" zu erhalten. Aber können Sie einfach nicht tun, weil linq-zu-sql berechtigterweise when you are trying to use a property that is not mapped to a column beschweren wird.

Also, wenn Sie keine Instanz verwenden können, was ist die nächstbeste Sache?

Nun, vielleicht ich auf einem statischen Ausdruck absetzen würde

public partial class Motorcycle : Vehicle 
{ 
    public static Expression<Func<Vehicle, bool>> IsNew { get { return (v) => v.Age <= 1; } } 
} 

public partial class Car : Vehicle 
{ 
    public static Expression<Func<Vehicle, bool>> IsNew { get { return (v) => v.Age <= 2; } } 
} 

, die Sie mögen

var newCars = db.Cars.Where(Car.IsNew).ToList(); 
var newMotorcycles = db.Motorcycles.Where(Motorcycle.IsNew).ToList(); 

verwenden können Oder man könnte die Logik außerhalb Ihre Anwendung ziehen und es tun in SQL Server als computed column, die ich persönlich denke, ist Ihre beste Option.

+0

Vielen Dank für die Antwort. Mein Hauptproblem mit Ihrem Vorschlag ist, dass ich den Typ nicht immer kenne, also werde ich am Ende typeof an vielen Stellen hinzufügen und Casting ausführen, bevor ich den IsNew-Ausdruck ausführe. Ich kam zu dem Schluss, dass ich es mit LINQ allein nicht schaffen kann, aber es gelang mir, einen irgendwie dreckigen Workaround zu finden: Ich benutzte LINQKit ExpandableQuery und erweiterte die gesamte Logik, um den richtigen Ausdruck auszuwählen. Auf diese Weise wird das Klassenfahrzeug niemals an LINQ gesendet und nur Auto und Motorrad werden dort behandelt. Nicht eine sehr saubere Lösung, obwohl – Sami

+0

Ich stimme Ihnen jedoch mit den berechneten Spalten, gespeicherte Prozeduren, Ansichten vollständig zu, um alle in SQL Server zu behandeln. Aber ich arbeite an einem bestehenden großen Projekt mit allen bereits in LINQ definierten Ausdrücken. Es wird Monate dauern, diese zu überschreiben und deshalb eine LINQ-Methode zu finden. – Sami

+0

Ich kann nicht helfen, aber ich denke, es gab eine Möglichkeit, wie Reflektion Ihnen in irgendeiner Weise geholfen haben könnte. Es ist ein ziemlich verwirrender Aspekt von C#, wenn Sie noch keine Erfahrung damit haben, ist es vielleicht nicht zeitsparend. Obwohl ich persönlich festgestellt habe, dass harte strukturelle Probleme wie diese oft mit Nachdenken gelöst werden können. – Shadetheartist

0

Mit der abstrakten Klasse können Sie ererbte Klassen erzwingen, um Ihre IsNew-Eigenschaft zu implementieren. Es ist nur eine Basisklasse. Sie müssen eine Implementierung von Auto oder Motorrad verwenden und dafür sollten Sie Ihr Vehicule-Objekt als Auto oder als Motorrad werfen, damit die richtige IsNew-Eigenschaft aufgerufen wird.

var vehicule = new Car(); 
+0

Die Frage ist über Linq-zu-SQL-Übersetzung von zwei Klassen mit Zuordnung zu Datenbank und einer abstrakten Elternklasse. Es ist nicht nur eine normale Vererbung/abstrakte Klasse. – Sami