Partager via


Suivi et requêtes No-Tracking

Le suivi du comportement détermine si Entity Framework Core conserve des informations sur une instance d’entité dans son suivi des modifications. Si une entité est suivie, toutes les modifications détectées dans l’entité sont conservées dans la base de données pendant SaveChanges. EF Core corrige également les propriétés de navigation entre les entités dans un résultat d'une requête suivie et les entités qui se trouvent dans le gestionnaire de modifications.

Remarque

Les types d’entités sans clé ne sont jamais suivis. Partout où cet article mentionne les types d’entités, il fait référence aux types d’entités qui ont une clé définie.

Conseil / Astuce

Vous pouvez afficher l’exemple de cet article sur GitHub.

Suivi des requêtes

Par défaut, les requêtes qui retournent des types d’entités sont en suivi. Une requête de suivi signifie que les modifications apportées aux instances d’entité sont conservées par SaveChanges. Dans l’exemple suivant, la modification de l’évaluation des blogs est détectée et conservée dans la base de données pendant SaveChanges:

var blog = await context.Blogs.SingleOrDefaultAsync(b => b.BlogId == 1);
blog.Rating = 5;
await context.SaveChangesAsync();

Lorsque les résultats sont retournés dans une requête de suivi, EF Core vérifie si l’entité est déjà dans le contexte. Si EF Core trouve une entité existante, la même instance est retournée, ce qui peut potentiellement utiliser moins de mémoire et être plus rapide qu’une requête sans suivi. EF Core ne remplace pas les valeurs actuelles et d’origine des propriétés de l’entité dans l’entrée avec les valeurs de base de données. Si l’entité est introuvable dans le contexte, EF Core crée une instance d’entité et l’attache au contexte. Les résultats de la requête ne contiennent aucune entité ajoutée au contexte, mais pas encore enregistrée dans la base de données.

Requêtes sans suivi

Les requêtes sans suivi sont utiles lorsque les résultats sont utilisés dans un scénario en lecture seule. Ils sont généralement plus rapides à exécuter, car il n’est pas nécessaire de configurer les informations de suivi des modifications. Si les entités récupérées à partir de la base de données n’ont pas besoin d’être mises à jour, une requête sans suivi doit être utilisée. Une requête individuelle peut être configurée comme sans suivi. Une requête sans suivi donne également des résultats en fonction des éléments de la base de données qui ignorent les modifications locales ou les entités ajoutées.

var blogs = await context.Blogs
    .AsNoTracking()
    .ToListAsync();

Le comportement de suivi par défaut peut être modifié au niveau de l’instance de contexte :

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = await context.Blogs.ToListAsync();

La section suivante explique quand une requête sans suivi peut être moins efficace qu’une requête de suivi.

Résolution de l'identité

Étant donné qu'une requête de suivi utilise le suivi des modifications, EF Core effectue la résolution des identités lors de ce type de requête. Lors de la matérialisation d'une entité, EF Core retourne la même instance d'entité à partir du gestionnaire de suivi des modifications s'il est déjà en cours de suivi. Si le résultat contient la même entité plusieurs fois, la même instance est retournée pour chaque occurrence. Requêtes sans suivi :

  • N’utilisez pas le suivi des modifications et ne faites pas de résolution d’identité.
  • Retourne une nouvelle instance de l’entité même lorsque la même entité est contenue dans le résultat plusieurs fois.

Le suivi et le suivi sans suivi peuvent être combinés dans la même requête. Autrement dit, vous pouvez avoir une requête sans suivi, qui effectue la résolution d’identité dans les résultats. Tout comme AsNoTracking l’opérateur interrogeable, nous avons ajouté un autre opérateur AsNoTrackingWithIdentityResolution<TEntity>(IQueryable<TEntity>). Il existe également une entrée associée ajoutée dans l’énumération QueryTrackingBehavior . Lorsque la requête pour utiliser la résolution d’identité est configurée sans suivi, un suivi des modifications autonome est utilisé en arrière-plan lors de la génération des résultats de la requête afin que chaque instance soit matérialisée une fois seulement. Étant donné que ce suivi des modifications est différent de celui du contexte, les résultats ne sont pas suivis par le contexte. Une fois la requête énumérée entièrement, le suivi des modifications sort de portée et est collecté par le ramasse-miettes selon les besoins.

var blogs = await context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .ToListAsync();

Configuration du comportement de suivi par défaut

Si vous changez le comportement de suivi pour de nombreuses requêtes, vous pouvez modifier la valeur par défaut à la place :

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying.Tracking;Trusted_Connection=True;ConnectRetryCount=0")
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}

Cela fait que toutes vos requêtes sont sans suivi par défaut. Vous pouvez toujours ajouter AsTracking pour effectuer un suivi des requêtes spécifiques.

Suivi et projections personnalisées

Même si le type de résultat de la requête n’est pas un type d’entité, EF Core effectue toujours le suivi des types d’entités contenus dans le résultat par défaut. Dans la requête suivante, qui retourne un type anonyme, les instances de Blog dans l'ensemble de résultats seront suivies.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, PostCount = b.Posts.Count() });

Si le jeu de résultats contient des types d’entités provenant de la composition LINQ, EF Core les suit.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });

Si le jeu de résultats ne contient aucun type d’entité, aucun suivi n’est effectué. Dans la requête suivante, nous renvoyons un type anonyme avec certaines des valeurs de l’entité (mais aucune instance du type d’entité réel). Aucune entité suivie ne résulte de la requête.

var blog = context.Blogs
    .Select(
        b =>
            new { Id = b.BlogId, b.Url });

EF Core prend en charge l’évaluation des clients dans la projection de niveau supérieur. Si EF Core matérialise une instance d’entité pour l’évaluation du client, elle sera suivie. Ici, étant donné que nous transmettons des entités blog à la méthode client StandardizeURL, EF Core suivra également les instances de blog.

var blogs = await context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
    .ToListAsync();
public static string StandardizeUrl(Blog blog)
{
    var url = blog.Url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

EF Core ne suit pas les instances d’entité sans clé contenues dans le résultat. Mais EF Core effectue le suivi de toutes les autres instances de types d’entités avec une clé en fonction des règles ci-dessus.

Versions précédentes

Avant la version 3.0, EF Core avait des différences dans la façon dont le suivi a été effectué. Les différences notables sont les suivantes :

  • Comme expliqué dans la page Client et Évaluation du serveur , EF Core a pris en charge l’évaluation du client dans n’importe quelle partie de la requête avant la version 3.0. L’évaluation du client a provoqué la matérialisation d’entités, qui ne faisaient pas partie du résultat. EF Core a donc analysé le résultat pour détecter les éléments à suivre. Cette conception a eu certaines différences comme suit :

    • L'évaluation du client dans la projection, qui a provoqué la matérialisation mais n'a pas retourné l'instance d'entité matérialisée, n'a pas été enregistrée. L’exemple suivant n’a pas suivi les blog entités.

      var blogs = await context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(
              blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
          .ToListAsync();
      
    • EF Core n’a pas suivi les objets sortis de la composition LINQ dans certains cas. L’exemple suivant n’a pas suivi Post.

      var blog = context.Blogs
          .Select(
              b =>
                  new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });
      
  • Chaque fois que les résultats de la requête contenaient des types d’entités sans clé, l’ensemble de la requête devenait sans suivi. Cela signifie que les types d’entités avec des clés, qui se trouvent dans le résultat n’ont pas été suivis non plus.

  • EF Core utilisait la résolution d'identité dans les requêtes sans suivi. Il a utilisé des références faibles pour suivre les entités qui avaient déjà été renvoyées. Par conséquent, si un jeu de résultats contenait plusieurs fois la même entité, vous obtiendriez la même instance pour chaque occurrence. Bien que si un résultat précédent avec la même identité était sorti de sa portée et avait été collecté par le ramasse-miettes, EF Core retournait une nouvelle instance.