Getrennte EntitätenDisconnected entities

Eine DbContext-Instanz verfolgt automatisch Entitäten nach, die von der Datenbank zurückgegeben wurden.A DbContext instance will automatically track entities returned from the database. An diesen Entitäten vorgenommene Änderungen werden nach dem Aufrufen von SaveChanges erkannt, und die Datenbank wird ggf. aktualisiert.Changes made to these entities will then be detected when SaveChanges is called and the database will be updated as needed. Weitere Einzelheiten finden Sie unter Grundlegendes zum Speichern und Zugehörige Daten.See Basic Save and Related Data for details.

Entitäten werden jedoch manchmal mit einer Kontextinstanz abgefragt und anschließend mit einer anderen Instanz gespeichert.However, sometimes entities are queried using one context instance and then saved using a different instance. Dies geschieht häufig in „getrennten“ Szenarios, wie z.B. einer Webanwendung, in welcher die Entitäten abgefragt werden, an den Client gesendet werden, geändert werden, in einer Anforderung zurück an den Server gesendet werden und anschließend gespeichert werden.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. In diesem Fall muss der zweiten Kontextinstanz bekannt sein, ob die Entitäten neu (Einfügung erforderlich) oder bereits vorhanden (Aktualisierung erforderlich) sind.In this case, the second context instance needs to know whether the entities are new (should be inserted) or existing (should be updated).

Tipp

Das in diesem Artikel verwendete Beispiel finden Sie auf GitHub.You can view this article's sample on GitHub.

Tipp

EF Core kann nur eine Instanz einer Entität mit einem bestimmten primären Schlüsselwert nachverfolgen.EF Core can only track one instance of any entity with a given primary key value. Dass dies ein Problem darstellt, kann verhindert werden, indem für die einzelnen Arbeitseinheiten kurzlebiger Kontext verwendet wird, wie z.B. dass der Kontext leer beginnt, über angefügte Entitäten verfügt, diese Entitäten speichert und der Kontext anschließend verworfen wird.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.

Identifizieren neuer EntitätenIdentifying new entities

Client identifiziert neue EntitätenClient identifies new entities

Am einfachsten ist, wenn der Client den Server darüber informiert, ob die Entität neu oder vorhanden ist.The simplest case to deal with is when the client informs the server whether the entity is new or existing. Die Anforderung zum Einfügen einer neuen Entität unterscheidet sich beispielsweise häufig von der Anforderung zum Aktualisieren einer vorhandenen Entität.For example, often the request to insert a new entity is different from the request to update an existing entity.

Im restlichen Teil dieses Abschnitts werden die Fälle behandelt, bei denen auf andere Weise bestimmt werden muss, ob eine Einfügung oder ein Update erforderlich ist.The remainder of this section covers the cases where it necessary to determine in some other way whether to insert or update.

Mit automatisch generierten SchlüsselnWith auto-generated keys

Mit dem Wert eines automatisch generierten Schlüssels kann häufig bestimmt werden, ob eine Entität eingefügt oder aktualisiert werden muss.The value of an automatically generated key can often be used to determine whether an entity needs to be inserted or updated. Wenn der Schlüssel nicht festgelegt worden ist (d.h., wenn er noch den CLR-Standardwert NULL, 0 (null) etc. aufweist), ist davon auszugehen, dass die Entität neu ist und eingefügt werden muss.If the key has not been set (i.e. it still has the CLR default value of null, zero, etc.), then the entity must be new and needs inserting. Wenn der Schlüsselwert bereits festgelegt worden ist, muss er andererseits zuvor gespeichert worden sein und nun aktualisiert werden.On the other hand, if the key value has been set, then it must have already been previously saved and now needs updating. Das heißt, wenn der Schlüssel einen Wert aufweist, wurde die Entität abgefragt, an den Client gesendet und nun für ein Update zurückgesendet.In other words, if the key has a value, then entity was queried, sent to the client, and has now come back to be updated.

Wenn der Entitätstyp bekannt ist, kann ohne großen Aufwand überprüft werden, ob ein nicht festgelegter Schlüssel vorhanden ist:It is easy to check for an unset key when the entity type is known:

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

EF verfügt jedoch auch über eine integrierte Möglichkeit, diesen Vorgang für einen beliebigen Entitäts- und Schlüsseltyp durchzuführen: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;

Tipp

Schlüssel werden festgelegt, sobald Entitäten vom Kontext nachverfolgt werden, selbst dann, wenn die Entität den Status „Hinzugefügt“ aufweist.Keys are set as soon as entities are tracked by the context, even if the entity is in the Added state. Dies ist hilfreich, wenn ein Graph mit Entitäten durchlaufen wird und entschieden wird, wie mit den einzelnen Entitäten umgegangen werden soll, z.B. bei der Verwendung der TrackGraph-API.This helps when traversing a graph of entities and deciding what to do with each, such as when using the TrackGraph API. Der Schlüsselwert sollte nur auf die hier dargestellte Weise verwendet werden, bevor ein Aufruf zum Nachverfolgen der Entität durchgeführt wird.The key value should only be used in the way shown here before any call is made to track the entity.

Mit anderen SchlüsselnWith other keys

Zum Identifizieren neuer Entitäten sind einige andere Mechanismen erforderlich, wenn Schlüsselwerte nicht automatisch generiert werden.Some other mechanism is needed to identify new entities when key values are not generated automatically. Hierfür gibt es zwei allgemeine Ansätze:There are two general approaches to this:

  • Abfrage für die EntitätQuery for the entity
  • Übergeben eines Flags vom ClientPass a flag from the client

Verwenden Sie für eine Abfrage für die Entität einfach die Find-Methode:To query for the entity, just use the Find method:

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

Die Anzeige des vollständigen Codes für die Übergabe eines Flags von einem Client ist nicht Gegenstand dieses Dokuments.It is beyond the scope of this document to show the full code for passing a flag from a client. In einer Web-App bedeutet dies in der Regel, dass für verschiedene Aktionen unterschiedliche Anforderungen durchgeführt werden, oder dass einige Status in der Anforderung übergeben und anschließend im Controller extrahiert werden.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.

Speichern einzelner EntitätenSaving single entities

Wenn bekannt ist, ob eine Einfügung oder ein Update erforderlich ist, kann entsprechend die Add- oder die Update-Methode verwendet werden: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();
}

Wenn die Entität automatisch generierte Schlüsselwerte verwendet, kann die Update-Methode in beiden Fällen verwendet werden: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();
}

Die Update-Methode markiert die Entität normalerweise für das Update, nicht für die Einfügung.The Update method normally marks the entity for update, not insert. Wenn die Entität über einen automatisch generierten Schlüssel verfügt und kein Schlüsselwert festgelegt wurde, wird die Entität jedoch stattdessen für eine Einfügung markiert.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.

Tipp

Dieses Verhalten wurde in EF Core 2.0 eingeführt.This behavior was introduced in EF Core 2.0. Bei früheren Releases muss immer explizit die Add- oder die Update-Methode ausgewählt werden.For earlier releases it is always necessary to explicitly choose either Add or Update.

Wenn die Entität keine automatisch generierten Schlüssel verwendet, muss die Anwendung entscheiden, ob die Entität eingefügt oder aktualisiert werden sollte. Beispiel: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();
}

Folgende Schritte müssen ausgeführt werden:The steps here are:

  • Wenn die Find-Methode NULL zurückgibt, enthält die Datenbank den Blog mit dieser ID noch nicht. Daher wird die Add-Methode aufgerufen, um die Datenbank für eine Einfügung zu markieren.If Find returns null, then the database doesn't already contain the blog with this ID, so we call Add mark it for insertion.
  • Wenn die Find-Methode eine Entität zurückgibt, ist diese in der Datenbank vorhanden, und der Kontext verfolgt nun die vorhandene Entität nachIf Find returns an entity, then it exists in the database and the context is now tracking the existing entity
    • Anschließend werden die Werte für sämtliche Eigenschaften dieser Entität mit der SetValues-Methode auf die vom Client stammenden Werte festgelegt.We then use SetValues to set the values for all properties on this entity to those that came from the client.
    • Beim SetValues-Aufruf wird die Entität entsprechend ihrer Markierung nach Bedarf aktualisiert.The SetValues call will mark the entity to be updated as needed.

Tipp

SetValues markiert nur die Eigenschaften als geändert, die andere Werte aufweisen als die Eigenschaften in der verfolgten Entität.SetValues will only mark as modified the properties that have different values to those in the tracked entity. Das heißt, wenn das Update gesendet wird, werden nur die Spalten aktualisiert, die tatsächlich geändert wurden.This means that when the update is sent, only those columns that have actually changed will be updated. (Und wenn keine Änderungen vorgenommen wurden, wird gar kein Update gesendet.)(And if nothing has changed, then no update will be sent at all.)

Arbeiten mit GraphenWorking with graphs

IdentitätsauflösungIdentity resolution

Wie oben bereits erwähnt, kann EF Core nur eine Instanz einer Entität mit einem bestimmten primären Schlüsselwert nachverfolgen.As noted above, EF Core can only track one instance of any entity with a given primary key value. Bei der Arbeit mit Graphen sollte der Graph idealerweise so erstellt werden, dass diese Invariante beibehalten wird. Zudem sollte der Kontext nur für eine Arbeitseinheit verwendet werden.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. Wenn der Graph Duplikate enthält, muss dieser verarbeitet werden, bevor er an EF gesendet wird, um mehrere Instanzen in eine zu konsolidieren.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. Dies ist möglicherweise nicht einfach, wenn Instanzen in Konflikt stehende Werte und Beziehungen aufweisen. Zur Vermeidung einer Konfliktauflösung sollte die Konsolidierung von Duplikaten folglich so schnell wie möglich in Ihrer Anwendungspipeline erfolgen.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.

Alle neuen/alle vorhandenen EntitätenAll new/all existing entities

Ein Beispiel für die Arbeit mit Graphen besteht in einer Einfügung oder Aktualisierung eines Blogs zusammen mit der zugehörigen Sammlung zugehöriger Beiträge.An example of working with graphs is inserting or updating a blog together with its collection of associated posts. Wenn alle Entitäten im Graph eingefügt werden sollen, oder wenn alle Entitäten aktualisiert werden sollen, entspricht der Prozess dem oben beschriebenen Prozess für einzelne Entitäten.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. Beispiel: Ein wie folgt erstellter Graph mit Blogs und Beiträgen: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"},
    }
};

kann wie folgt eingefügt werden:can be inserted like this:

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

Beim Aufruf zum Hinzufügen werden der Blog und sämtliche Beiträge für eine Einfügung markiert.The call to Add will mark the blog and all the posts to be inserted.

Gleichermaßen kann die Update-Methode verwendet werden, wenn sämtliche Entitäten in einem Graph aktualisiert werden müssen: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();
}

Der Blog und alle zugehörigen Beiträge werden für ein Update markiert.The blog and all its posts will be marked to be updated.

Mischung aus neuen und vorhandenen EntitätenMix of new and existing entities

Bei automatisch generierten Schlüsseln kann erneut für Einfügungen und Updates die Update-Methode verwendet werden. Dies gilt auch dann, wenn der Graph eine Mischung aus einzufügenden und zu aktualisierenden Entitäten enthält: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();
}

Die Update-Methode markiert eine beliebige Entität im Graph, Blog oder Beitrag für eine Einfügung, wenn diese keinen festgelegten Schlüsselwert aufweist, während alle anderen Entitäten für eine Aktualisierung markiert werden.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.

Wie bisher können eine Abfrage und eine Verarbeitungsschritte verwendet werden, wenn keine automatisch generierten Schlüssel verwendet werden: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();
}

Behandlung von LöschvorgängenHandling deletes

Die Behandlung von Löschvorgängen kann kompliziert sein, da die Abwesenheit einer Entität häufig bedeutet, dass diese gelöscht werden sollte.Delete can be tricky to handle since often the absence of an entity means that it should be deleted. Eine Möglichkeit für den Umgang hiermit besteht in der Verwendung von „vorläufigen Löschvorgängen“. Dabei wird die Entität als gelöscht markiert, statt tatsächlich gelöscht zu werden.One way to deal with this is to use "soft deletes" such that the entity is marked as deleted rather than actually being deleted. Löschvorgänge entsprechen anschließend Updates.Deletes then becomes the same as updates. Vorläufige Löschvorgänge können mit Abfragefiltern implementiert werden.Soft deletes can be implemented in using query filters.

Bei Löschvorgängen mit dem Wert „TRUE“ wird häufig eine Erweiterung des auszuführenden Abfragemusters verwendet. Dies ist im Grunde genommen eine GraphDiff-Methode. Zum Beispiel: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

Die Add-, Attach- und Update-Methoden verwenden intern einen Diagrammdurchlauf, in dem bestimmt wird, ob die einzelnen Entitäten als „Hinzugefügt“ (für Einfügung), „Geändert“ (für Update), „Unverändert“ (nichts unternehmen) oder als „Gelöscht“ (für Löschung) markiert werden sollen.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). Dieser Mechanismus wird über die TrackGraph-API verfügbar gemacht.This mechanism is exposed via the TrackGraph API. Angenommen beispielsweise, der Client sendet einen Graph mit Entitäten zurück und legt für jede Entität ein Flag fest, das angibt, wie die Entität behandelt werden soll.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. Anschließend kann dieses Flag mit TrackGraph verarbeitet werden: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();
}

Zur Vereinfachung des Beispiels werden die Flags nur als Teil der Entität angezeigt.The flags are only shown as part of the entity for simplicity of the example. Normalerweise wären die Flags Teil eines DTO oder eines anderen in der Anforderung enthaltenen Status.Typically the flags would be part of a DTO or some other state included in the request.