Entités déconnectéesDisconnected entities

Une instance de DbContext suit automatiquement les entités retournées à partir de la base de données.A DbContext instance will automatically track entities returned from the database. Les modifications apportées à ces entités seront ensuite détectées lorsque SaveChanges est appelé, et la base de données sera mise à jour en fonction des besoins.Changes made to these entities will then be detected when SaveChanges is called and the database will be updated as needed. Consultez Enregistrement de base et Données associées pour plus d’informations.See Basic Save and Related Data for details.

Toutefois, parfois, les entités sont interrogées par une instance de contexte, puis enregistrées à l’aide d’une autre instance.However, sometimes entities are queried using one context instance and then saved using a different instance. Cela se produit souvent dans les scénarios « déconnectés », par exemple une application web où les entités sont interrogées, envoyées au client, modifiées, envoyées sur le serveur dans une demande et puis enregistrées.This often happens in "disconnected" scenarios such as a web application where the entities are queried, sent to the client, modified, sent back to the server in a request, and then saved. Dans ce cas, la deuxième instance de contexte doit savoir si les entités sont nouvelles (doivent être insérées) ou existantes (doivent être mises à jour).In this case, the second context instance needs to know whether the entities are new (should be inserted) or existing (should be updated).

Conseil

Vous pouvez afficher cet exemple sur GitHub.You can view this article's sample on GitHub.

Conseil

EF Core peut suivre une seule instance d’une entité avec une valeur de clé primaire donnée.EF Core can only track one instance of any entity with a given primary key value. La meilleure façon d’éviter ce problème consiste à utiliser un contexte de courte durée de vie pour chaque unité de travail, de sorte que le contexte commence vide, ait des entités associées, enregistre ces entités, puis soit supprimé.The best way to avoid this being an issue is to use a short-lived context for each unit-of-work such that the context starts empty, has entities attached to it, saves those entities, and then the context is disposed and discarded.

Identification des nouvelles entitésIdentifying new entities

Le client identifie de nouvelles entitésClient identifies new entities

Le cas le plus simple à gérer est lorsque le client indique au serveur si l’entité est nouvelle ou existante.The simplest case to deal with is when the client informs the server whether the entity is new or existing. Par exemple, la requête d’insertion d’une nouvelle entité diffère souvent de la requête pour mettre à jour une entité existante.For example, often the request to insert a new entity is different from the request to update an existing entity.

Le reste de cette section couvre les cas où il est nécessaire de déterminer, d’une autre manière, s’il faut insérer ou mettre à jour.The remainder of this section covers the cases where it necessary to determine in some other way whether to insert or update.

Avec des clés générées automatiquementWith auto-generated keys

La valeur d’une clé générée automatiquement peut souvent être utilisée pour déterminer si une entité doit être insérée ou mise à jour.The value of an automatically generated key can often be used to determine whether an entity needs to be inserted or updated. Si la clé n’a pas été définie (autrement dit si elle a toujours la valeur CLR par défaut de null, zéro, etc.), alors l’entité doit être nouvelle et a besoin d’être insérée.If the key has not been set (that is, it still has the CLR default value of null, zero, etc.), then the entity must be new and needs inserting. En revanche, si la valeur de clé a été définie, l’entité doit avoir déjà été précédemment enregistrée et doit être mise à jour.On the other hand, if the key value has been set, then it must have already been previously saved and now needs updating. En d’autres termes, si la clé a une valeur, cette entité a été interrogée, envoyée au client et est maintenant revenue pour être mise à jour.In other words, if the key has a value, then the entity was queried, sent to the client, and has now come back to be updated.

Il est facile de rechercher une clé non définie lorsque le type d’entité est connu :It is easy to check for an unset key when the entity type is known:

public static bool IsItNew(Blog blog) 
    => blog.BlogId == 0;

Toutefois, EF a également un moyen intégré de faire cela pour n’importe quel type d’entité et type de clé :However, EF also has a built-in way to do this for any entity type and key type:

public static bool IsItNew(DbContext context, object entity) 
    => !context.Entry(entity).IsKeySet;

Conseil

Les clés sont définies dès que les entités sont suivies par le contexte, même si l’entité est dans l’état Ajouté.Keys are set as soon as entities are tracked by the context, even if the entity is in the Added state. Cela est utile lors du parcours d’un graphique d’entités pour décider quelles actions effectuer avec chacune, par exemple lors de l’utilisation de l’API TrackGraph.This helps when traversing a graph of entities and deciding what to do with each, such as when using the TrackGraph API. La valeur de clé doit uniquement être utilisée de la manière illustrée ici avant qu’un appel soit envoyé pour effectuer le suivi de l’entité.The key value should only be used in the way shown here before any call is made to track the entity.

Avec d’autres clésWith other keys

Un autre mécanisme est nécessaire pour identifier les nouvelles entités lorsque les valeurs des clés ne sont pas générées automatiquement.Some other mechanism is needed to identify new entities when key values are not generated automatically. Il existe deux approches générales pour cela :There are two general approaches to this:

  • Requête pour l'entitéQuery for the entity
  • Passez un indicateur à partir du clientPass a flag from the client

Pour rechercher l’entité, utilisez simplement la méthode Find :To query for the entity, just use the Find method:

public static bool IsItNew(BloggingContext context, Blog blog)
    => context.Blogs.Find(blog.BlogId) == null;

L’affichage du code complet pour le passage d’un indicateur depuis un client n’entre pas dans la portée de ce document.It is beyond the scope of this document to show the full code for passing a flag from a client. Dans une application web, cela consiste généralement à effectuer des demandes différentes pour différentes actions, ou à passer un état dans la demande, puis l’extraire dans le contrôleur.In a web app, it usually means making different requests for different actions, or passing some state in the request then extracting it in the controller.

Enregistrement d’entités uniquesSaving single entities

Si on sait si une insertion ou une mise à jour est nécessaire, vous pouvez ajouter Add ou Update de manière appropriée :If it is known whether or not an insert or update is needed, then either Add or Update can be used appropriately:

public static void Insert(DbContext context, object entity)
{
    context.Add(entity);
    context.SaveChanges();
}

public static void Update(DbContext context, object entity)
{
    context.Update(entity);
    context.SaveChanges();
}

Toutefois, si l’entité utilise les valeurs de clé générées automatiquement, la méthode Update peut être utilisée pour les deux cas :However, if the entity uses auto-generated key values, then the Update method can be used for both cases:

public static void InsertOrUpdate(DbContext context, object entity)
{
    context.Update(entity);
    context.SaveChanges();
}

Normalement, la méthode Update marque l’entité pour la mise à jour, et non l’insertion.The Update method normally marks the entity for update, not insert. Toutefois, si l’entité a une clé générée automatiquement, et qu’aucune valeur de clé n’a été définie, l’entité est automatiquement marquée pour insertion.However, if the entity has a auto-generated key, and no key value has been set, then the entity is instead automatically marked for insert.

Conseil

Ce comportement a été introduit dans EF Core 2.0.This behavior was introduced in EF Core 2.0. Pour les versions antérieures, il est toujours nécessaire de choisir explicitement Add ou Update.For earlier releases it is always necessary to explicitly choose either Add or Update.

Si l’entité n’utilise pas les clés générées automatiquement, l’application doit décider si l’entité doit être insérée ou mise à jour. Par exemple :If the entity is not using auto-generated keys, then the application must decide whether the entity should be inserted or updated: For example:

public static void InsertOrUpdate(BloggingContext context, Blog blog)
{
    var existingBlog = context.Blogs.Find(blog.BlogId);
    if (existingBlog == null)
    {
        context.Add(blog);
    }
    else
    {
        context.Entry(existingBlog).CurrentValues.SetValues(blog);
    }

    context.SaveChanges();
}

Les étapes ici sont :The steps here are:

  • Si Find retourne null, la base de données ne contient pas encore le blog avec cet ID, nous appelons donc Add pour le marquer pour insertion.If Find returns null, then the database doesn't already contain the blog with this ID, so we call Add mark it for insertion.
  • Si la recherche retourne une entité, il existe dans la base de données et le contexte suit maintenant l’entité existanteIf Find returns an entity, then it exists in the database and the context is now tracking the existing entity
    • Ensuite, nous utilisons SetValues pour définir les valeurs de toutes les propriétés de cette entité sur celles envoyées par le client.We then use SetValues to set the values for all properties on this entity to those that came from the client.
    • L’appel à SetValues marque l’entité à mettre à jour en fonction des besoins.The SetValues call will mark the entity to be updated as needed.

Conseil

SetValues marque uniquement comme modifiées les propriétés qui ont des valeurs différentes de celles de l’entité suivie.SetValues will only mark as modified the properties that have different values to those in the tracked entity. Cela signifie que lorsque la mise à jour est envoyée, seules les colonnes qui ont été modifiées seront mises à jour.This means that when the update is sent, only those columns that have actually changed will be updated. (Et si rien n’a changé, alors aucune mise à jour ne sera envoyée du tout).(And if nothing has changed, then no update will be sent at all.)

Travail avec les graphiquesWorking with graphs

Résolution de l'identitéIdentity resolution

Comme indiqué ci-dessus, EF Core peut suivre une seule instance d’une entité avec une valeur de clé primaire donnée.As noted above, EF Core can only track one instance of any entity with a given primary key value. Lorsque vous travaillez avec des graphiques, le graphique doit idéalement être créé de sorte que cet invariant est géré, et le contexte doit être utilisé pour une seule unité de travail.When working with graphs the graph should ideally be created such that this invariant is maintained, and the context should be used for only one unit-of-work. Si le graphique contient des doublons, il sera nécessaire de traiter le graphique avant de l’envoyer à EF pour consolider les instances multiples en une seule.If the graph does contain duplicates, then it will be necessary to process the graph before sending it to EF to consolidate multiple instances into one. Cela peut être difficile lorsque les instances ont des valeurs et relations en conflit. La consolidation des doublons doit donc être effectuée dès que possible dans le pipeline de votre application afin d’éviter la résolution des conflits.This may not be trivial where instances have conflicting values and relationships, so consolidating duplicates should be done as soon as possible in your application pipeline to avoid conflict resolution.

Toutes les entités nouvelles/toutes les entités existantesAll new/all existing entities

Un exemple d’utilisation des graphiques est l’insertion ou la mise à jour d’un blog ainsi que de sa collection de billets associés.An example of working with graphs is inserting or updating a blog together with its collection of associated posts. Si toutes les entités dans le graphique doivent être insérées, ou toutes doivent être mises à jour, le processus est identique à celui décrit ci-dessus pour les entités uniques.If all the entities in the graph should be inserted, or all should be updated, then the process is the same as described above for single entities. Par exemple, un graphique de blogs et publications créé comme suit :For example, a graph of blogs and posts created like this:

var blog = new Blog
{
    Url = "http://sample.com",
    Posts = new List<Post>
    {
        new Post {Title = "Post 1"},
        new Post {Title = "Post 2"},
    }
};

peut être inséré comme suit :can be inserted like this:

public static void InsertGraph(DbContext context, object rootEntity)
{
    context.Add(rootEntity);
    context.SaveChanges();
}

L’appel à Add marque le blog et tous les billets à insérer.The call to Add will mark the blog and all the posts to be inserted.

De même, si toutes les entités dans un graphique doivent être mises à jour, alors Update peut être utilisé :Likewise, if all the entities in a graph need to be updated, then Update can be used:

public static void UpdateGraph(DbContext context, object rootEntity)
{
    context.Update(rootEntity);
    context.SaveChanges();
}

Le blog et tous ses billets seront marqués pour être mis à jour.The blog and all its posts will be marked to be updated.

Combinaison d’entités nouvelles et existantesMix of new and existing entities

Avec les clés générées automatiquement, Update est de nouveau utilisable à la fois pour les insertions et pour les mises à jour, même si le graphique contient un mélange d’entités qui nécessitent l’insertion et d’autres qui nécessitent la mise à jour :With auto-generated keys, Update can again be used for both inserts and updates, even if the graph contains a mix of entities that require inserting and those that require updating:

public static void InsertOrUpdateGraph(DbContext context, object rootEntity)
{
    context.Update(rootEntity);
    context.SaveChanges();
}

Update marque une entité dans le graphique, le blog ou un billet, pour insertion si elle ne dispose pas d’un ensemble clé-valeur, tandis que toutes les autres entités sont marquées pour mise à jour.Update will mark any entity in the graph, blog or post, for insertion if it does not have a key value set, while all other entities are marked for update.

Comme précédemment, lorsque vous n’utilisez pas les clés générées automatiquement, vous pouvez utiliser une requête et un traitement :As before, when not using auto-generated keys, a query and some processing can be used:

public static void InsertOrUpdateGraph(BloggingContext context, Blog blog)
{
    var existingBlog = context.Blogs
        .Include(b => b.Posts)
        .FirstOrDefault(b => b.BlogId == blog.BlogId);

    if (existingBlog == null)
    {
        context.Add(blog);
    }
    else
    {
        context.Entry(existingBlog).CurrentValues.SetValues(blog);
        foreach (var post in blog.Posts)
        {
            var existingPost = existingBlog.Posts
                .FirstOrDefault(p => p.PostId == post.PostId);

            if (existingPost == null)
            {
                existingBlog.Posts.Add(post);
            }
            else
            {
                context.Entry(existingPost).CurrentValues.SetValues(post);
            }
        }
    }

    context.SaveChanges();
}

Gestion des suppressionsHandling deletes

La suppression peut être compliquée à gérer, car souvent l’absence d’une entité signifie qu’elle droit être supprimée.Delete can be tricky to handle since often the absence of an entity means that it should be deleted. Une façon de gérer cela consiste est d’utiliser des « suppressions récupérables » par exemple en marquant l’entité comme supprimée plutôt que la supprimer réellement.One way to deal with this is to use "soft deletes" such that the entity is marked as deleted rather than actually being deleted. Les suppressions s’apparentent alors à des mises à jour.Deletes then becomes the same as updates. Les suppressions récupérables peuvent être implémentées à l’aide de filtres de requête.Soft deletes can be implemented in using query filters.

Pour les vraies suppressions, il est courant d’utiliser une extension du modèle de requête pour effectuer ce qui est essentiellement une comparaison de graphique. Exemple :For true deletes, a common pattern is to use an extension of the query pattern to perform what is essentially a graph diff. For example:

public static void InsertUpdateOrDeleteGraph(BloggingContext context, Blog blog)
{
    var existingBlog = context.Blogs
        .Include(b => b.Posts)
        .FirstOrDefault(b => b.BlogId == blog.BlogId);

    if (existingBlog == null)
    {
        context.Add(blog);
    }
    else
    {
        context.Entry(existingBlog).CurrentValues.SetValues(blog);
        foreach (var post in blog.Posts)
        {
            var existingPost = existingBlog.Posts
                .FirstOrDefault(p => p.PostId == post.PostId);

            if (existingPost == null)
            {
                existingBlog.Posts.Add(post);
            }
            else
            {
                context.Entry(existingPost).CurrentValues.SetValues(post);
            }
        }

        foreach (var post in existingBlog.Posts)
        {
            if (!blog.Posts.Any(p => p.PostId == post.PostId))
            {
                context.Remove(post);
            }
        }
    }

    context.SaveChanges();
}

TrackGraphTrackGraph

En interne, Add, Attach et Update utilisent la traversée de graphique en déterminant pour chaque entité si elle doit être marquée comme Added (à insérer), Modified (à mettre à jour), Unchanged (ne rien faire), ou Deleted (à supprimer).Internally, Add, Attach, and Update use graph-traversal with a determination made for each entity as to whether it should be marked as Added (to insert), Modified (to update), Unchanged (do nothing), or Deleted (to delete). Ce mécanisme est exposé via l’API TrackGraph.This mechanism is exposed via the TrackGraph API. Par exemple, supposons que, lorsque le client envoie un graphique d’entités, il définit certains indicateurs sur chaque entité indiquant comment elle doit être gérée.For example, let's assume that when the client sends back a graph of entities it sets some flag on each entity indicating how it should be handled. TrackGraph peut ensuite être utilisé pour traiter cet indicateur :TrackGraph can then be used to process this flag:

public static void SaveAnnotatedGraph(DbContext context, object rootEntity)
{
    context.ChangeTracker.TrackGraph(
        rootEntity,
        n =>
        {
            var entity = (EntityBase)n.Entry.Entity;
            n.Entry.State = entity.IsNew
                ? EntityState.Added
                : entity.IsChanged
                    ? EntityState.Modified
                    : entity.IsDeleted
                        ? EntityState.Deleted
                        : EntityState.Unchanged;
        });

    context.SaveChanges();
}

Les indicateurs sont uniquement affichés dans le cadre de l’entité pour simplifier l’exemple.The flags are only shown as part of the entity for simplicity of the example. En général, les indicateurs feraient partie d’un DTO ou d’un autre état inclus dans la demande.Typically the flags would be part of a DTO or some other state included in the request.