Hoe ‘deferred execution’ je het leven zuur kan maken
383

Wanneer je gebruik maakt van Linq, is het belangrijk om het verschil te weten tussen deferred execution en immediate execution. Onoplettend gebruik van deferred execution kan je leven behoorlijk zuur maken. Zeker als er IQueryables vanuit Entity Framework en Linq to objects door elkaar gebruikt worden.

Wat is “deferred execution”

Stel je hebt een simpele repository waarmee je personen kunt opvragen op basis van hun achternaam en een persoon kunt toevoegen. En stel dat je deze repository als volgt gebruikt in code:

Je zou nu waarschijnlijk verwachten dat de Linq query wordt uitgevoerd waar de repository wordt aangeroepen en dat je vervolgens een statische lijst met personen krijgt. In werkelijkheid wordt de query echter pas uitgevoerd wanneer de foreach wordt aangeroepen.

Dit kan aangetoond worden door tussen het moment waarop de lijst wordt opgehaald en de foreach-lus een extra persoon aan de repository toe te voegen.

De output van deze code is:

Voorkomen van “deferred execution”

Om te voorkomen dat het systeem de query pas later uitvoert, kun je vanuit de methode de query direct uitvoeren door bijvoorbeeld het ToList commando. Nadat we dit gedaan hebben ontvangt de aanroepende klasse een gematerialiseerde lijst van personen en zal het toevoegen van een persoon aan de repository geen invloed meer hebben op de uitkomst van de foreach loop.

Wat is nu het probleem wat we hiermee oplossen
Op papier is het toepassen van “deferred execution” toch juist een heel goed idee? Dat klopt, en als het heel precies wordt toegepast dan kan het ook in je voordeel werken. Het probleem is echter dat het in de praktijk heel moeilijk blijkt om zo precies te werken. Zeker als je met een groter team, of meerdere teams aan een stuk software werkt, blijkt dat er heel veel problemen kunnen ontstaan door onverwacht gedrag van de applicatie.

Het gedrag van deferred execution breekt “the principle of least surprise” doordat je als gebruiker van een klasse feitelijk niet kunt weten of de klasse de query nu direct uitvoert of niet. Daarnaast past het niet goed binnen de SOLID principes waarbij het gedrag van een klasse voorspelbaar moet zijn en niet afhankelijk moet zijn van de implementatie van de aanroepende partij (Liskov principle).

De uitdaging wordt alleen nog maar groter wanneer teams gebruik maken van Entity Framework en IQueryables door hun applicatie heen gebruiken. Dit maakt het haast onmogelijk om grip te houden op de queries die op de database worden uitgevoerd en het debuggen van een expression tree is ook niet bepaald een pretje.

Wat is ons advies
• Gebruik deferred execution alleen binnen de scope van een klasse (private). Hierdoor kun je bijvoorbeeld complexere queries met entity framework optimaliseren tot één query.
• Zorg dat de publieke methodes van je objecten altijd gematerialiseerde objecten teruggeven en geen query objecten (en zeker geen IQueryable).
• Zet bij gebruik van Entity Framework Lazy loading altijd uit om te voorkomen dat er onder water queries worden uitgevoerd zonder dat de ontwikkelaar zich daar bewust van is.

Auteur: Menno Jongerius, Bergler Competence Center © 2017