it-swarm.com.de

Null oder leeres Objekt, wenn die Abfrage LINQ to Entities nichts zurückgibt

Angenommen, ich habe eine LINQ-Abfrage wie folgt:

application = CreditDatabase
                        .Applications
                        .Select(Mapper.Map<Application>) 
                        .Where(c => c.uID == urID)
                        .DefaultIfEmpty().First();

Es gibt null zurück, wenn die LINQ-Abfrage eine leere Ergebnismenge zurückgibt. Ist das richtig". Ist es besser, ein leeres Objekt zurückzugeben? Wenn ja, wie kann ich das tun? Dieser Link besagt, dass es nicht möglich ist, einen Referenztyp ungleich Null zurückzugeben: https://msdn.Microsoft.com/en-us/library/bb360179 (v = vs.110) .aspx

3
w0051977

Datenbankabfragen geben das Ergebnis sets zurück. Eine leere Menge ist eine vernünftige Antwort; Es bedeutet, dass Sie keine der gesuchten Dinge haben.

Ich würde argumentieren, dass Ihr Code bei der Kanonisierung des Ergebnisses zu weit geht.

Ihr Code (irgendwo kurz nach dieser Zeile) prüft, ob die gerade gesuchte Anwendung vorhanden ist oder nicht, sodass Sie die nachfolgende bedingte Logik (in Ihrer Frage nicht gezeigt) in Ihrem Code nicht durch Ausführen der Funktion .FirstOrDefault().

Sie können - und ich argumentiere sollte - stattdessen: nach der leeren Menge suchen (dh die Größe der Ergebnismenge verwenden, möglicherweise mit .Any()) um zu sehen, ob die Anwendung existiert. Das ist wesentlich sauberer, als die leere Menge in die Standardeinstellung umzuwandeln und dann die erste und später die Null zu überprüfen, um festzustellen, ob die interessierende Anwendung vorhanden ist.

Anstatt so weit zu gehen:

application = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID)
                    .DefaultIfEmpty().First();
if ( application == null ) {
   // handle missing application, maybe add it...
} else {
    // use the found application
}

Lassen Sie die Standardbehandlung weg. stattdessen:

search = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID);
if ( ! search.Any () ) {
    // handle missing application, maybe add it...
} else {
    application = search.Single ();
    // use the found application
}

Nachtrag: Im obigen application = search.Single () habe ich .Single() anstelle von .First() verwendet, außer wenn die nicht leere Ergebnismenge Wenn mehr als eine Übereinstimmung vorliegt, ist dies sinnvoll: Es handelt sich um eine Suche nach einer exakten Übereinstimmung des eindeutigen Primärschlüssels (ansonsten wissen wir, wie @JacquesB hervorhebt, da die Abfrage nicht geordnet ist, nicht wirklich, was wir sind. ' behalten und was wir mit .First()) wegwerfen.

8
Erik Eidt

Es kommt darauf an!

Aber zuerst eine Klarstellung: .DefaultIfEmpty().First() kann einfacher geschrieben werden als .FirstOrDefault(), was dasselbe tut - gibt das erste Element zurück oder null, wenn das Ergebnis leer ist. Aber Sie haben hier wahrscheinlich einen Fehler: First() gibt an, dass Sie möglicherweise mehrere Artikel haben und den ersten Artikel auswählen möchten - aber da keine Bestellung erforderlich ist, erhalten Sie einen zufälliger Gegenstand. Dies ist wahrscheinlich nicht das, was Sie wollen! Wenn Sie wirklich erwarten, dass es mehrere Übereinstimmungen geben könnte, geben Sie einfach die Liste der Übereinstimmungen zurück. Wenn Sie ein einzelnes Element erwarten, verwenden Sie stattdessen Single(), und wenn Sie entweder ein einzelnes Element oder nichts erwarten, verwenden Sie SingleOrDefult(), das auch null zurückgibt, wenn es kein Element gibt Spiel.

Nun zur interessanten Frage: Was kann man von einer Methode zurückgeben, die entweder ein einzelnes Objekt oder nichts findet?

Je nachdem, was Sie erreichen möchten, gibt es verschiedene Optionen:

  • Geben Sie null zurück, wenn das Element nicht gefunden wird. Vorteile: Das ist wirklich einfach. Nachteile: Der Client muss nach Null suchen, was leicht vergessen wird, da das Typsystem diese Prüfung nicht erzwingt. Dies kann zu einem schwer zu debuggenden NullReferenceExceptions auf der ganzen Linie führen. Nicht empfohlen.

  • Lösen Sie eine Ausnahme aus, wenn das Objekt nicht gefunden wird. Vorteile: Einfach und sicher. Nachteile: Sollte nur verwendet werden, wenn ein Fehler angezeigt wird, wenn das Objekt nicht vorhanden ist.

  • Erstellen Sie eine separate Methode, mit der Sie überprüfen können, ob das Objekt vorhanden ist (z. B. if (ApplicationExists(id))...). Diese Methode gibt einen Booleschen Wert zurück. Vorteile: Löschen Sie den Code auf der Client-Seite. Nachteile: Wird die Datenbank zweimal treffen, es sei denn, Sie verwenden eine Art Caching, so dass es teuer sein kann. Auch mehr Code.

  • Verwenden Sie das Muster TryXXX. Gibt einen Booleschen Wert zurück, der angibt, ob das Objekt vorhanden ist, und das Objekt (falls vorhanden) in einem out -Parameter platziert. Vorteile: Dieses Muster ist bereits aus say Dictionary.TryGetValue() bekannt, sodass die Leser nicht verwirrt werden. Nachteil: Der Parameter out führt zu etwas hässlichem Code, obwohl dies in C # 6 verbessert wird.

  • Geben Sie ein Option<T> Art. Dies erfordert, dass der Client das Objekt explizit überprüft und auspackt. Vorteile: Viel sicherer und eleganter als die Verwendung von Null. Nachteil: C # hat kein eingebautes Option<T> tippe, du musst einen finden oder selbst schreiben.

Das einzige, was Sie nicht tun sollten:

  • gibt eine "leere" Instanz der Klasse zurück.
2
JacquesB

Ich mag es, null neu abzustimmen, wenn etwas nicht gefunden wird. Für mich macht es Sinn.

Geben Sie mir den ersten Artikel, der diesen Kriterien entspricht. Es gibt keine, hier ist eine Null. Ihr Code löst eine Ausnahme aus, wenn er versucht, ihn zu verwenden.

Eine Alternative, wenn Sie nicht gerne auf Nullen oder leere Objekte testen möchten, besteht darin, immer eine Liste von Objekten zurückzugeben.

Auf diese Weise können Sie für jeden auf der Liste eine machen und es tut natürlich nichts, wenn nichts gefunden wird.

Es gibt keine Möglichkeit, das Standardverhalten zu überschreiben, Sie können jedoch die Nullverkettung verwenden

var x = Applications.FirstOrDefault() ?? new Application();

Ps. Verschieben Sie Ihr Mapping nach der where-Klausel!

0
Ewan