Top 10 Fehler–#5: Failing to consider the underlying objects in a LINQ statement

16. Juli 2014

Dies ist Teil 5 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Heute zum Thema… immer noch LINQ.

 

Common Mistake #5: Failing to consider the underlying objects in a LINQ statement

Eine große Stärke von LINQ ist, dass ich mit der gleichen Syntax und Mächtigkeit Abfragen im Speicher, gegen die Datenbank, XML Dokumente, OData- und andere Services – kurz: alles, wofür es einen LINQ Provider gibt – durchführen kann.

Eine große Falle von LINQ ist, dass ich mit der gleichen Syntax und Mächtigkeit Abfragen schreiben kann, ohne dass die Eigenheiten des Providers auffällig werden.

Eine noch größere Falle von LINQ ist, dass ich mit der gleichen Syntax und Mächtigkeit Abfragen schreiben kann, ohne dass die Fähigkeiten des Providers berücksichtigt werden.

 

Patrick zeigt dazu ein sehr einfaches Beispiel – ein String-Vergleich, der von LINQ-to-Objects natürlich anders durchgeführt wird, als bei der Übersetzung in SQL und Ausführung durch die Datenbank.

Das ist richtig, und doch kratzt es nur an der Oberfläche. Nicht alle Provider unterstützen alle Möglichkeiten, die mit LINQ vorgesehen sind, und LINQ bietet nicht jedes Feature aller Datenquellen an. Beliebtes Beispiel sind Abfragen gegen die Datenbank mit LIKE (hier, hier). Spätestens wenn man sich an Sonderimplementierungen bindet ist an einen Wechsel des Providers ohnehin nicht mehr zu denken.

Verheimlichen lässt sich der Provider spätestens dann nicht mehr, wenn die LINQ-Abfrage an einen Kontext gebunden ist, wie das bei LINQ2SQL und LINQ2EF der Fall ist:

   1: using (var context = new ProductContext()) 

   2: {     

   3:     // Perform data access using the context 

   4:     return context.Products.Where(p => p.ID == 4711);

   5: }

Das geht sowas von daneben…

Manchmal sind es unerwartete Ergebnisse. Manchmal Exceptions vom Provider weil er bestimmte Dinge nicht umsetzen kann. Oder miese Performance, weil der LINQ-Ausdruck so hingebogen wurde, dass er scheinbar tut – nur eben im Speicher, statt in der Datenbank.

Aber wie auch immer sich das Problem darstellt – es ist eine häufig zu beobachtende Fehlerquelle. (Grund schient mir zu sein, dass jedem Provider die Fähigkeiten und Eigenschaften der Programmiersprache unterstellt werden. Nachvollziehbar, aber trotzdem ein Trugschluss.)

Daher die klare Empfehlung:

  • Wie Patrick schon sagt: Man sollte sich bei LINQ immer über die Datenquelle im Klaren sein.
  • Das betrifft nicht nur die Abfragen selbst, sondern auch die Frage nach lazy/eager evaluation.
  • Darüber hinaus sollte man durchaus auch mal prüfen, welche Abfragen unter der Oberfläche entstehen, etwa indem man sich das abgesetzte SQL Statement anschaut.

Besondere Steigerung erfährt dieses Problem übrigens dann, wenn unterschiedliche Datenquellen in einem LINQ-Ausdruck gemischt werden…