LINQ To SQL, DLINQ pour les intimes, est à la fois un langage de requêtes et un mapping O/R simplifié. Ce billet se concentre sur cet aspect ORM et tente de dresser une liste de bonnes pratiques d’utilisation.

Les bonnes pratiques dépendent en fait du type de requêtes : en lecture, ou en mise à jour.

Requêtes en lecture

Suivi des modifications

Les requêtes de type SELECT permettent de récupérer des entités à partir de la base de données. Les entités LINQ sont par la suite surveillées, afin de garder une trace des diverses modifications effectuées. Le suivi des modifications est assuré par le mécanisme d’object tracking. Ce dernier fait intervenir un traitement relativement coûteux, qui n’est utile que si l’on modifie les entités par la suite. Dans ce dernier cas, nous aurions intérêt à désactiver l’object tracking en attribuant la valeur false à la propriété ObjectTrackingEnabled de notre DataContext.

Compilation des requêtes

Les requêtes LINQ suivent, toutes, la même séquence d’exécution à savoir :

  • création de l’arbre d’exécution,
  • conversion en SQL,
  • exécution de la requête,
  • récupération des données,
  • conversion en entités LINQ.

L’utilisation des requêtes pré-compilées permet de faire l’économie des deux premières étapes. Si nous utilisons fréquemment la même requête LINQ, nous avons intérêt à éviter la première et deuxième étape. La classe CompiledQuery et sa méthode statique Compile permettent de compiler une requête LINQ une fois, et de la conserver pour une utilisation ultérieure.

Chargement différé

Le mécanisme de chargement différé (ou lazy loading) est présent dans la plupart des ORM. Les développeurs ont souvent recours à ce mécanisme pour alléger la quantité d’information récupérée, ou pour réduire le nombre de requêtes envoyées à la base de données. Cette pratique n’est pas toujours mise en œuvre à bon escient. C’est notamment le cas quand les développeurs se soucient plus de la taille des données récupérées. Ils activent alors le lazy loading, faisant ainsi croître le nombre de requêtes, ce qui peut pénaliser les performances.

Il est donc capital de bien étudier les deux possibilités et de tester leur efficacité. Le chargement différé peut être fait à l’aide de l’objet DataLoadOptions, qui fournit des méthodes comme Load, LoadWith et surtout AssociateWith. Cette dernière méthode a l’avantage d’offrir un chargement conditionnel selon des critères que nous pouvons fixer.

Requêtes de mise à jour

À la différence des requêtes de type SELECT, les requêtes type UPDATE sont concernées par le problème des accès concurrentiels en écriture. LINQ to SQL gère cet aspect, soit selon une logique pessimiste, soit selon une logique optimiste.

La logique pessimiste est la plus simple d’utilisation, mais elle n’est pas toujours adaptée. La logique optimiste requiert nettement plus de traitements, et peut nuire aux performances. Il peut donc être intéressant de désactiver la logique optimiste. C’est notamment le cas lorsque deux modifications ont lieu simultanément, et qu’il est fonctionnellement acceptable de ne conserver que la seconde. La désactivation de la logique optimiste se fait en modifiant l’attribut UpdateCheck sur les propriétés des entités, et en lui affectant la valeur UpdateCheck.Never.

Plus loin

Toutes ces pratiques sont à prendre en considération lors de l’optimisation des requêtes de récupération et de modification des données. Il en existe bien sûr d’autres, mais elles ne sont généralement applicables que dans des situations bien spécifiques.