Fermer

janvier 29, 2023

Quoi de neuf dans EF Core 7.0

Quoi de neuf dans EF Core 7.0


Avec l’arrivée récente de .NET 7, de nouvelles fonctionnalités sont également disponibles pour EF Core dans la dernière version 7. Consultez ce billet de blog pour les principales actualités avec des exemples pratiques.

.NET 7 a été officiellement publié récemment et a apporté des améliorations significatives à plusieurs domaines de .NET tels que les serveurs et l’exécution, les API minimales, Blazor et autres. Ce qui a également reçu des améliorations, c’est EF Core.

Dans cet article de blog, nous allons vérifier les principales actualités de Microsoft ORM et comment nous pouvons l’utiliser dans la pratique.

Premiers pas avec EF Core

Si vous êtes nouveau dans le développement ASP.NET Core, je vous suggère de lire cet article Principes de base de .NET : ORM (mappage relationnel d’objets) qui explique ce qu’est un ORM et comment fonctionne EF Core (Entity Framework Core).

💡 Tous les exemples présentés dans cet article sont accessibles dans ce référentiel : code source.

Les nouvelles méthodes ExecuteUpdate et ExecuteDelete

Avant la sortie d’EF Core 7, le moyen le plus courant d’enregistrer les modifications apportées à la base de données était via la méthode d’extension SaveChanges, qui suit par défaut les modifications apportées aux entités et envoie des mises à jour à la base de données.

Les modifications apportées par SaveChanges sont mappées et envoyées uniquement aux propriétés et relations qui ont changé. De plus, les entités contrôlées sont synchronisées avec les modifications envoyées à la base de données.

Ce processus est un moyen efficace d’envoyer tout type de modification à la base de données. Après tout, en plus des fonctions mentionnées ci-dessus, elles sont également regroupées par lots pour réduire le nombre d’accès à la base de données.

Malgré son efficacité, il n’est pas toujours nécessaire de suivre les modifications comme c’est le cas avec la méthode traditionnelle SaveChanges. Ainsi, EF7 apporte deux nouvelles méthodes qui effectuent des modifications et des suppressions d’enregistrements sans charger les entités en mémoire, ce qui se traduit par des gains de performances grâce à des modifications et des exclusions plus efficaces.

Cependant, il est important de considérer que :

  • Les modifications doivent être spécifiées explicitement car elles ne sont pas automatiquement détectées par EF Core.
  • Les entités suivies ne seront pas synchronisées avec la base de données.
  • Des commandes supplémentaires peuvent devoir être envoyées dans le bon ordre pour ne pas violer les restrictions de la base de données, telles que la suppression de clés étrangères.

Par conséquent, les nouvelles méthodes ExecuteUpdate et ExecuteDelete ne remplacent pas SaveChanges, elles apportent simplement plus de possibilités lorsque vous travaillez avec des bases de données.

Exemple d’utilisation de la méthode ExecuteUpdate

Les méthodes ExecuteUpdate et ExecuteUpdateAsync sont très similaires à la méthode ExecuteDelete. Cependant, pour effectuer une mise à jour, il est nécessaire d’informer quelles propriétés doivent être mises à jour et comment cela sera fait, ce qui peut être via un ou plusieurs appels à la méthode SetProperty.

Voici un exemple d’utilisation de la méthode ExecuteUpdate :

   await db.Posts
    .Where(p => p.Id == "3fa85f64-5717-4562-b3fc-2c963f66afa6")
    .ExecuteUpdateAsync(s => s
    .SetProperty(b => b.AuthorName, "John Smith")
    .SetProperty(b => b.Title, b =>  "EF7 is here!")
    .SetProperty(b => b.Text, b =>  "	")
    .SetProperty(b => b.LastUpdateDate, "2022-30-11 17:29:46.5028235"));

Notez que pour exécuter la mise à jour, le paramètre qui indique quels enregistrements doivent recevoir la modification est passé à la méthode d’extension Where, qui dans ce cas est l’identifiant de l’enregistrement. Ensuite, la méthode ExecuteUpdateAsync est appelée avec la méthode SetProperty qui définit les propriétés de la base de données qui recevront la modification et la valeur qu’elle recevra.

Le SQL suivant est exécuté :

update Posts
set AuthorName = 'John Smith', 
Title = 'EF7 is here!', 
Text = 'Lorem ipsum dolor sit amet...', 
LastUpdateDate = '2022-30-11 17:29:46.5028235'
where Id = '88A7FC2B-55B5-4002-AE75-A125C90E54BD'

Exemple d’utilisation de la méthode ExecuteDelete

Les méthodes ExecuteDelete ou ExecuteDeleteAsync suppriment immédiatement toutes les entités de la base de données qui se trouvent dans les paramètres communiqués au DbSet.

L’exemple ci-dessous montre comment la méthode ExecuteDeleteAsync peut être utilisée, à la fois dans la condition d’un seul enregistrement et dans la condition de plusieurs.

Suppression d’un seul enregistrement :

await db.Posts.Where(p => p.Id == "3fa85f64-5717-4562-b3fc-2c963f66afa6").ExecuteDeleteAsync();

Le SQL suivant est exécuté :

delete from Posts where Id = '3fa85f64-5717-4562-b3fc-2c963f66afa6'

Suppression de plusieurs enregistrements :

await db.Posts.Where(p => p?.Text.Contains(".NET")).ExecuteDeleteAsync();

Le SQL suivant est exécuté :

delete from Posts where Text like '%.NET%'

Colonnes JSON

L’utilisation de colonnes JSON dans des bases de données relationnelles peut être une excellente idée, car grâce à elles, il est possible de filtrer et de classer les données à travers des éléments de document, ce qui permet aux bases de données relationnelles d’acquérir les caractéristiques des bases de données NoSQL, créant ainsi une sorte d’hybride entre les deux technologies.

EF7 prend en charge nativement les colonnes JSON, ce qui permet de mapper les types .NET aux documents JSON. Cela inclut également les requêtes LINQ qui peuvent être utilisées dans les agrégations et seront converties en constructions de requête appropriées pour JSON, et il est également possible de mettre à jour et d’enregistrer les modifications apportées aux documents JSON.

Un détail important est que ce mappage n’est actuellement possible que pour SQL SERVER mais la prise en charge de SQLite est déjà prévue pour les futures versions d’EF. Les bases de données PostgreSQL et Pomelo MySQL prennent déjà en charge JSON.

Mappage des colonnes JSON

Vous trouverez ci-dessous un exemple de conversion d’un type .NET en JSON à l’aide de la classe ContactDetails.

public class Author
{
    public Guid Id { get; set; }
    public string? Name { get; set; }
    public ContactDetails? Contact { get; set; }
}

public class ContactDetails
{
    public Address Address { get; set; } = null!;
    public string? Phone { get; set; }
}

public class Address
{
    public Address(string street, string city, string postcode, string country)
    {
        Street = street;
        City = city;
        Postcode = postcode;
        Country = country;
    }

    public string Street { get; set; }
    public string City { get; set; }
    public string Postcode { get; set; }
    public string Country { get; set; }
}

Notez que la classe ContactDetails est un objet de la classe Author, donc quand EF7 fera la conversion, elle sera transformée en une chaîne de données au format JSON.

Pour que la conversion ait lieu, il suffit que dans la classe qui implémente la classe DbContext, la méthode OnModelCreating soit ajoutée – et dans celle-ci, la configuration qui déclarera quelles colonnes de la table recevront le document JSON, ce qui se fait via le ToJson méthode de rallonge. Vous pouvez le voir dans le code ci-dessous :

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Author>().OwnsOne(
            author => author.Contact, ownedNavigationBuilder =>
            {
                ownedNavigationBuilder.ToJson();
                ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
            });
    }

Voyez dans l’image ci-dessous comment était l’enregistrement après avoir été enregistré dans la base de données :

Colonne SQL JSON

Requêtes sur les colonnes JSON

L’interrogation des colonnes JSON est très simple : il suffit d’utiliser LINQ, comme les autres types d’agrégation.

Notez dans l’exemple ci-dessous où la valeur d’une propriété (ville) est filtrée dans le JSON.

app.MapGet("/auhtor/getAuhtorsByCity/{city}", async (AuthorDb db, string city) =>
{
    var authorsByCity = await db.Authors.Where(author => author.Contact.Address.City == city).ToListAsync();

    if (!authorsByCity.Any())
        return Results.NotFound();

    return Results.Ok(authorsByCity);
})
.WithName("GetAuhtorsByCity")
.WithOpenApi();

Le code ci-dessus génère la requête SQL SERVER suivante. Notez que l’expression JSON_VALUE est utilisée pour extraire la valeur JSON de la colonne Contact.

SELECT [a].[Id], [a].[Name], JSON_QUERY([a].[Contact],'$')
FROM [Authors] AS [a]
WHERE CAST(JSON_VALUE([a].[Contact],'$.Address.City') AS nvarchar(max)) = N'Juneau'

Mise à jour des colonnes JSON

Pour mettre à jour les colonnes JSON, les méthodes SaveChanges et SaveChangesAsync peuvent être utilisées. Pour les modifications importantes, l’intégralité du document sera mise à jour, comme illustré dans l’exemple ci-dessous :


    var authorExists = await db.Authors.Where(author => author.Id == id).FirstOrDefaultAsync();

    authorExists.Contact = new() { Address = new("912 Park Allen", "Ket chikan", "99901", "USA"), Phone = "(907) 247-9199" };

    await db.SaveChangesAsync();

Ainsi, tout l’objet à mettre à jour est passé en paramètre et le code SQL suivant est exécuté :

Executed DbCommand (4ms) [Parameters=[@p0='{"address": {"street": "912 Park Alles", "city": "Ket chikan", "postcode": "99901", "country": "USA"}, "phone": "(907) 247-9199"}' (Size = 119), @p1='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
UPDATE [Authors] SET [Contact] = @p0
OUTPUT 1
WHERE [Id] = @p1;

Il est également possible de mettre à jour une seule propriété JSON. Le code C# suivant :

    var authorExists = await db.Authors.Where(author => author.Id == id).FirstOrDefaultAsync();

    authorExists.Contact.Address.Street = "1523 Stellar Dr";

    await db.SaveChangesAsync();

… exécute le code SQL suivant :

Executed DbCommand (4ms) [Parameters=[@p0='["1523 Stellar Dr"]' (Size = 19), @p1='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
UPDATE [Authors] SET [Contact] = JSON_MODIFY([Contact], 'strict $.Address.Street', JSON_VALUE(@p0, '$[0]'))
OUTPUT 1
WHERE [Id] = @p1;

Méthode SaveChanges plus rapide

Les méthodes SaveChanges et SaveChangesAsync ont été améliorées dans EF7 : leurs performances peuvent être jusqu’à quatre fois plus rapides que dans EF Core 6.0 !

La majeure partie de ce gain de performances est due à la réduction du nombre d’allers-retours vers la base de données et à une génération SQL plus rapide.

Un détail qui mérite notre attention est la valeur de la méthode SaveChanges, car elle peut effectuer des transactions et des mappages complexes via les fonctions EF Core.

Voici quelques-uns des avantages de l’utilisation de SaveChanges :

  • Regroupez plusieurs commandes d’insertion, de mise à jour et de suppression pour réduire les allers-retours dans la base de données.
  • Découvrez si une transaction explicite est requise ou non.
  • Déterminez dans quel ordre insérer, mettre à jour et supprimer les entités afin que les contraintes de la base de données ne soient pas violées.
  • Assurez-vous que les valeurs générées par la base de données sont efficacement renvoyées et réensemencées aux entités.
  • Définissez automatiquement les valeurs de clé étrangère à l’aide des valeurs générées pour les clés primaires.
  • Détecter les conflits de concurrence.
  • Détectez différents systèmes de base de données et assurez-vous que les commandes correctes et efficaces sont envoyées à chacun.

Nouvelles options de requête

EF7 a apporté de nouvelles fonctionnalités aux requêtes telles que GroupBy en tant qu’opérateur final. L’exemple ci-dessous montre comment le regroupement peut être effectué à l’aide de la méthode d’extension GroupBy :

    var groupByAuthor = db.Posts.GroupBy(p => p.AuthorName).ToList();

Cette requête génère le code SQL suivant :

SELECT [p].[Id], [Title].[Text], [p].[AuthorName], [p].[CreationDate],[p].[LastUpdateDate]
FROM [Posts] AS [p]
ORDER BY [b].[AuthorName]

Notez que ce type de GroupBy n’est pas directement converti en SQL, donc EF Core effectue le regroupement sur les résultats renvoyés.

Conclusion

Cet article a présenté certaines des principales fonctionnalités disponibles dans la version 7.0 récemment publiée d’EF Core. En plus de celles-ci, il existe encore d’autres améliorations qui peuvent être vérifiées dans leur intégralité dans la documentation Microsoft : EF Core 7.0—Nouveautés.

Sans aucun doute, EF Core est un outil formidable pour travailler avec des bases de données en .NET, et maintenant il est encore plus complet dans cette nouvelle version.




Source link