Nouveautés de Entity Framework Core 2.0

Qu’est-ce que EF Core ?

EF Core 2.0 est un framework qui permet de faire le lien entre le modèle d’une base de données relationnelle et un modèle orienté objet en mémoire. EF vous affranchit d’écrire le code SQL qui permet de charger des données de la BD vers les objets et d’appliquer les changements apportés à vos objets dans la BD.

EF Core est léger, extensible et portable. Il tourne sous Windows, Linux, MacOs, iPhone, Android et toute la gamme des appareils Windows : Xbox, Windows Phone, Surface etc

La prochaine version à venir est la 2.0. Elle s’appuiera sur .NET Core 2.0. A ce propos, Rowan Miller a été assez clair sur le fait que les efforts d’innovations se concentreront sur Entity Framework Core alors que Entity Framework 6 (disponible uniquement sous Windows et Full framework) bénéficiera uniquement des correctifs nécessaires à son bon fonctionnement.

EF_landscape

Quel avenir pour Entity Framework?

Entity Framework a été introduit pour la première fois avec .NET Framework 3.5. Il y a eu un certain nombre de livraisons depuis et nous sommes maintenant en version 6.1.

Entity Framework cible le Framework .NET et est donc uniquement destiné aux applications Windows. EF Core est une implémentation plus récente qui se base sur .NET Standard et donc destiné à une audience plus large.

Les deux framework continueront leurs évolutions en parallèle. Il n’y a donc pas lieu de paniquer d’autant qu’actuellement il y a 90% d’utilisateurs de Entity Framework pour 10% d’utilisateurs de EF Core.

EF_EFCore

Les nouvelles fonctionnalités

Les fonctions

EF Core introduit un nouveau point d’extensibilité qui sont les fonctions. Les fonctions sont accessibles via une propriété appelée Functions sur l’objet EF. Elles permettent d’utiliser des opérateurs ou des fonctions SQL directement dans une requête LINQ. Microsoft ouvrira le bal avec l’implémentation d’une fonction Like qui correspond au LIKE du langage SQL.

Voici un exemple d’utilisation:

var villes = from v in context.Villes
             where EF.Functions.Like(v.Nom, “%QU”)
             select v

Global query filter

Un filtre de requête global est une expression qu’on peut associer à une entité dans le but qu’elle soit automatiquement injectée à chaque fois qu’une requête SELECT concernant cette entité est exécutée.

Par exemple si dans un contexte de vente, je travaille toujours avec des articles en stock et disponibles à la vente, je pourrai écrire ce qui suit :

public class ContexteVente : DbContext
{
	public DbSet<Article> Articles { get; set; }
	public DbSet<Vente> Ventes { get; set; }
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.Entity<Articles>()
			.HasQueryFilter(a => a. EstDisponibleVente && a.Quantite > 0);
	}
}

Flexible Mapping

Il arrive parfois qu’une entité possède un attribut qu’il ne fait pas de sens d’exposer au travers d’une propriété publique. Par exemple cet attribut pourrait avoir une utilisation réservée pour le système et on ne désire pas donner la possibilité au développeur de la manipuler. Le flexible mapping fait référence au fait qu’il sera possible de faire le lien entre une colonne d’une table et un champ privé d’une entité.

Dans mon exemple précédent, la propriété EstDisponibleVente est un booléen que je pourrais décider de ne pas rendre accessible en modification. Je pourrais donc dégrader cette propriété publique vers un champ privé et ensuite introduire une propriété conceptuelle dans le modèle pour quand même être en mesure de l’utiliser sous la couverte.

Voici une illustration :

public class Article
{
	bool _estDisponibleVente;
	int Quantite {get; set;}
	//...
}

public class ContexteVente : DbContext
{
	public DbSet<Article> Articles { get; set; }
	public DbSet<Vente> Ventes { get; set; }

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.Entity<Articles>()
			.Property<bool>("EstDisponibleVente");
			.HasField("_estDisponibleVente");

		modelBuilder.Entity<Articles>()
			.HasQueryFilter(a => EF.Property(a,"EstDisponibleVente") && a.Quantite > 0);
	}
}

Compiled queries

Il sera bientôt possible de compiler une requête LINQ via du code. Vous vous en doutez, l’objectif sera de gagner en performance quand on se trouve à exécuter une requête un grand nombre de fois.

Le processus qui permet de déterminer l’ordre SQL associé à une requête LINQ se fait en 4 étapes :

  1. Construire l’arbre de la requête LINQ
  2. Calculer un hash de l’arbre
  3. Utiliser ce hash pour interroger la cache pour récupérer la requête SQL correspondante
  4. Si la requête SQL n’est pas dans la cache, alors la construire et la placer dans la cache

Les étapes 3 et 4 sont optimales puisque la requête SQL sera construite une seule fois pour plusieurs exécutions. Par contre, dans EF Core 1.1, les étapes 1 et 2 sont répétées à chaque exécution.

La compilation des requêtes permettent de s’assurer que les étapes 1 et 2 ne sont exécutées qu’une seule fois.

Voici un exemple de code qui montre comment précompiler:

private static Func _customerById =
	EF.CompileQuery((CustomerContext db, int id) =>
		db.Customers
		.Include(c => c.Address)
		.Single(c => c.Id == id));
...

using (var db = new CustomerContext())
{
	var customer = _customerById(db, 147);
}

Context pooling

Il s’agit encore là d’une optimisation interne du framework par rapport à l’exécution de requêtes. EF Core 1.1 crée systématiquement un nouveau contexte à chaque nouvelle exécution d’une requête LINQ. Dans 2.0 il sera possible d’activer l’utilisation d’un pool de contextes au sein de l’injecteur de dépendance. De cette manière chaque contrôleur de requête se fera injecter une instance de contexte existante plutôt que dans créer une.

Cette optimisation s’active en appelant la méthode AddDbContextPool sur la classe ServiceCollectionen remplacement d’une appel à AddDbContext.

Durant la démonstration, un test de charge de requêtes LINQ sans pool de contexte donnait à une vitesse de traitement d’un peu plus ou moins 9000 requêtes par seconde. L’activation du pool permettait de monter aux environs de 11200 requêtes par seconde.

Cette optimisation n’est pas active par défaut parce qu’elle pourrait avoir un impact sur votre code. En effet EF Core nettoie l’instance du contexte avant de le replacer dans le pool. Ceci implique que l’optimisation rend impossible l’utilisation du contexte comme un moyen de persister de l’information entre deux requêtes. Pour des scénarios avancés d’utilisation du contexte, l’optimisation ne fonctionne pas!

Ce billet est un résumé de la session donnée par Rowan Miller lors du Build 2017 sur les nouveautés de Entity Framework Core 2.0.