LINQ Coding Guidelines #9–Die richtigen Methoden verwenden

8. April 2015

Ähnlich ist nicht gleich, und auch wenn manche Aufrufe in LINQ ähnlich aussehen, können sie in Sonderfällen durchaus relevante Unterschiede mit sich bringen.

Empfehlung: In LINQ-Abfragen sollten Methoden verwendet werden, die der Intention entsprechen.

 

 

Beispiele aus der Praxis:

   1: static string GetOSVersion()

   2: {

   3:     var name = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem")

   4:         .Get()

   5:         .OfType<ManagementObject>()

   6:         .Select(x => x.GetPropertyValue("Caption"))

   7:         .First();

   8:     return name != null ? name.ToString() : "Unknown";

   9: }

Und:

   1: User user = userRep.GetAll().Where(u => u.UserName == userName).SingleOrDefault();

Was ist das Problem?

Die Methoden First() und Single() sind zwar ähnlich, aber eben nicht identisch. Analoges gilt für

Cast<>() und OfType<>(). Technisch hat das – im Regelfall – keine Konsequenzen (von kleinen Effizienzgewinnen mal abgesehen), aber die inhaltliche Aussage ist eine andere:

  • Wenn ich First() aufrufe will ich das erste Element einer Collection, implizit das einzige, wenn mein Filter und das Datenmodell das vorsehen. Wenn ich hingegen Single() aufrufe, dann will ich zusätzlich verifizieren, dass das tatsächlich das einzige Element meiner Abfrage ist.

    Problem dabei: Eine Prüfung der Datenkonsistenz ist nicht die Aufgabe einer normalen Abfrage (abgesehen davon, dass da sicher mehr zu tun wäre). So etwas muss in der Datenbank über Constraints sichergestellt oder von der Geschäftslogik beim Schreiben verifiziert werden.

  • Wenn ich Daten in einer (aus Sicht von LINQ) untypisierten Collection habe, dann nutze ich Cast<>() um das zu casten. Wenn ich hingegen OfType<>() aufrufe, dann gehe ich davon aus, dass in der Collection auch Objekte anderen Typs enthalten kann, die ich ausfiltern möchte. Denkbar, aber eher selten und auf den Sonderfall von Ableitungshierarchien im Resultset beschränkt.

    Problem: Im Regelfall ist das ohne Auswirkung. Bis sich tatsächlich mal ein Fehler einschleicht, der durch die Wahl der falschen Methode nicht erkannt wird und Datenmüll produziert.

Ergo: Wenn es ähnliche Funktionen gibt, hat das normalerweise einen Grund. Man sollte hier genau hinschauen, welche Methode die richtige ist. Schlampigkeit rächt sich irgendwann.