Dynamisch LINQ queries maken met Lambda expressies

In de data access laag van m’n projecten werd het nu toch wel een drukke boel met verschillende LINQ to SQL queries. Aangezien je namelijk niet goed dynamische LINQ-queries kunt maken, moet je eigenlijk voor iedere ‘uitzondering’ een aparte query maken. Je krijgt dan zoiets als dit:

if (criteria.PeriodeVan != null && criteria.PeriodeTot != null) 
{ 
    var query = (from post in dataContext.GetTable() 
                    where post.Item.CreatieDD >= criteria.PeriodeVan && 
                    post.Item.CreatieDD <= criteria.PeriodeTot 
                    orderby post.Item.CreatieDD descending 
                    select post)
                .ToArray(); 
    if (criteria.TopList > 0) 
    { 
        return query.Take(criteria.TopList); 
    } 
    else 
    { 
        return query; 
    } 
} 
else 
{ 
    var query = from post in dataContext.GetTable() 
    orderby post.Item.CreatieDD descending 
    select post; 
    if (criteria.TopList > 0) 
    { 
        return query.Take(criteria.TopList); 
    } 
    else 
    { 
        return query; 
    } 
}

Dit is een hele lap code voor eigenlijk maar 2 acties, namelijk een periode kiezen en een selectie van de bovenste items. Voor mijn relatief kleine projecten is dat wel te doen, maar bij grotere projecten zeker niet. Zelfs dit vind ik al lastig qua onderhoud. Nu ben ik vandaag op zoek gegaan naar een goed alternatief.

Als vrij snel kwam ik op een Chris Rock z’n weblog waar hij dit probleem ook beschrijft in de post die hier is te vinden: https://www.rocksthoughts.com/blog/archive/2008/01/24/linq-to-sql-dynamic-queries.aspx Eerst komt hij met het voorstel om de Dynamic LINQ Query Library te gebruiken. Deze had ik zelf ook al een keer gezien, maar dat is eigenlijk iets wat je totaal niet wilt gaan gebruiken. Je krijgt dan gewoon tekst queries in je code welke niet meer door je compiler worden gecontroleerd. Dit haalt Chris ook al aan. Daarna begint hij over de LINQ Predicate Builder.

Dit is eigenlijk precies wat je wilt gebruiken, of in ieder geval wat mij momenteel goed lijkt. Nu vind ik het ietwat overbodig om de hele LINQKit te downloaden en in m’n projecten te stoppen, dus ben ik zelf maar aan de slag gegaan om m’n eigen implementatie hier aan te geven. Copy-pasten vond ik ook een beetje doelloos, aangezien ik het toch iets anders wil dan dat ze op de LINQ Predicate Builder pagina beschrijven ( https://www.albahari.com/nutshell/predicatebuilder.html ).
Omdat Chris Rock z’n voorbeeld in VB.Net was heb ik het zelf nog moeten omschrijven naar een correcte C# implementatie. Waar ik nu zelf op uit ben gekomen is het volgende:

var query = dataContext.GetTable() as IQueryable; 
query = query.OrderByDescending(p => p.Item.CreatieDD); 
if (criteria.PeriodeVan != null && criteria.PeriodeTot != null) 
{ 
    query = query.Where(p => p.Item.CreatieDD >= criteria.PeriodeVan && p.Item.CreatieDD <= criteria.PeriodeTot); 
} 
if (criteria.CategorieID > 0 ) 
{ 
    query = query.Where(p => p.CategorieID == criteria.CategorieID); 
} 
if (criteria.TopList > 0) 
{ 
    query = query.Take(criteria.TopList); 
} 
return query.ToArray();

Op zich nog een redelijke lap code, maar een stuk beter onderhoudbaar als wat ik eerst had. Eerst definieer je de query variabele als een IQueryable. Daarna zorg je dat de nodige filters aan worden gekoppeld. Pas op het einde doe ik een query.ToArray() om er voor te zorgen dat de LINQ-query gelijk wordt uitgevoerd.

Hopelijk lever ik hierdoor niets (of weinig) in op de performance, maar dat zal moeten blijken in de toekomst.


Share

comments powered by Disqus