Loupe

Entity Framework Core : exécuter des requêtes SQL à la main

Entity Framework est vraiment génial pour gagner en productivité mais il faut se l'avouer, il fait quand même des fois du grand n'importe quoi sur certaines requêtes et les performances en sont dégradées de manière significative. Dans cet article, nous verrons comment exécuter une requête SQL faite à la main tout en continuant de profiter du mapping d'entité.

L'idée de base est donc d'utiliser EF de façon générale sur notre projet mais d'être capable d'écrire des requêtes performantes lorsque cela est nécessaire. On bénéficie ainsi du meilleur des deux mondes !

Définition d'une entité "Light"

La première optimisation permise par cette technique consiste à ne ramener de la base que ce qui est nécessaire. J'avais d'ailleurs déjà parlé de cette technique dans le cadre d'optimisation SQLite sur UWP.

Pour réaliser cela avec EF Core, on va simplement définir une classe ne contenant que les propriétés à lire. J'appelle cela des LightDTO car ils servent uniquement à transférer des informations de la BDD vers notre applicatif.

public class TvShowLightDTO
{
        public int Id { get; set; }
        public string Name { get; set; }
        public int NumberOfEpisode { get; set; }
}

Il faudra ensuite la faire connaître de notre contexte EntityFramework et on va utiliser une des nouveautés d'EFCore 2.1 pour cela : les DbQuery. Il suffit de déclarer une propriété de type DbQuery<T> sur votre contexte :

public class MonContext : DbContext
{
  public DbQuery<TvShowLightDTO> TvShowLightDTOs { get; set; }
}

L'avantage majeure de cette technique est que notre entité n'a pas à être une table de notre base de données.

Appel SQL

Pour faire une requête SQL, il suffit alors d'utiliser cette propriété et d'appeler la méthode FromSQL en passant la requête en paramètre.

var rawSqlString = "SELECT Id, Name, NumberOfEpisode"
                               + " FROM TvShowTracker tvs  ";

var results = await 
   MonContext.TvShowLightDTOs.FromSql(rawSqlString)
  .ToListAsync(cancellationToken: cancellationToken);

Il est possible de passer des paramètres et pour cela, on le fait de manière propre en utilisant des SQLParameters pour éviter les injections SQL :

var id = new SqlParameter("@IdTarget", "222");
var rawSqlString = "SELECT Id, Name, NumberOfEpisode"
                               + " FROM TvShowTracker "
                               + " WHERE Id=@IdTarget ";

var results = await 
   MonContext.TvShowLightDTOs
   .FromSql(rawSqlString, id)
   .ToListAsync(cancellationToken: cancellationToken);

On me souffle dans l'oreillette que l'on peut aussi utiliser les string interpolations dans FromSQL depuis EF Core 2.0 sans passer par des paramètres SQL. Sous le capot, EF fera bien une requête paramètrée pour vous (encore une fois, uniquement à partir de la version 2.0).

var id =  "222";

var results = await 
   MonContext.TvShowLightDTOs
   .FromSql("SELECT Id, Name, NumberOfEpisode"
                               + " FROM TvShowTracker "
                               + $" WHERE Id={id}")
   .ToListAsync(cancellationToken: cancellationToken);

Comment faire avant EF CORE 2.1 ?

Les DBQuery n'existant pas, il est obligatoire de passer par un appel à FromSQL sur le DbSet d'une entité existant en base. Il faudra cependant faire attention à ce que votre requête SQL retourne toutes les colonnes des entités requêtes. Petite astuce de Thomas Ouvrétricher avec des  "AS") pour retourner une fausse valeur pour une colonne. Par exemple pour renvoyer une valeur pour colonne "NumberOfEpisodes" inexistante sur ma table TvShowTrackerSansNumberOfEpisode :

 var rawSqlString = "SELECT Id, Name, 0 AS NumberOfEpisode"
          + " FROM TvShowTrackerSansNumberOfEpisode";

var tt = await 
   MonContext.TvShows.FromSql(rawSqlString)
  .ToListAsync(cancellationToken: cancellationToken);

Il faudra cependant avoir une entité approchant du type de résultat que vous voulez lire en base pour que cette méthode soit possible.

Happy coding !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus