Entitäten explizit nachverfolgenExplicitly Tracking Entities

Jede DbContext-Instanz verfolgt Änderungen nach, die an Entitäten vorgenommen wurden.Each DbContext instance tracks changes made to entities. Diese nachverfolgten Entitäten bestimmen wiederum die Änderungen an der Datenbank, wenn SaveChanges aufgerufen wird.These tracked entities in turn drive the changes to the database when SaveChanges is called.

Entity Framework Core (EF Core)-Änderungs Nachverfolgung funktioniert am besten, wenn die gleiche DbContext Instanz verwendet wird, um Entitäten abzufragen und durch Aufrufen von zu aktualisieren SaveChanges .Entity Framework Core (EF Core) change tracking works best when the same DbContext instance is used to both query for entities and update them by calling SaveChanges. Der Grund hierfür ist, dass EF Core den Status von abgefragten Entitäten automatisch nachverfolgt und dann alle Änderungen erkennt, die an diesen Entitäten vorgenommen werden, wenn SaveChanges aufgerufen wird.This is because EF Core automatically tracks the state of queried entities and then detects any changes made to these entities when SaveChanges is called. Diese Vorgehensweise wird in Änderungsnachverfolgung in EF Corebehandelt.This approach is covered in Change Tracking in EF Core.

Tipp

In diesem Dokument wird davon ausgegangen, dass die Entitäts Zustände und die Grundlagen EF Core Änderungs Nachverfolgung verstanden werden.This document assumes that entity states and the basics of EF Core change tracking are understood. Weitere Informationen zu diesen Themen finden Sie unter Änderungsnachverfolgung in EF Core .See Change Tracking in EF Core for more information on these topics.

Tipp

Sie können den gesamten Code in diesem Dokument ausführen und debuggen, indem Sie den Beispielcode von GitHub herunterladen.You can run and debug into all the code in this document by downloading the sample code from GitHub.

Tipp

Der Einfachheit halber werden in diesem Dokument synchrone Methoden wie z. B. SaveChanges anstatt ihrer asynchronen Entsprechungen wie SaveChangesAsync verwendet und darauf verwiesen.For simplicity, this document uses and references synchronous methods such as SaveChanges rather their async equivalents such as SaveChangesAsync. Das Aufrufen und Warten auf die asynchrone Methode kann ersetzt werden, sofern nicht anders angegeben.Calling and awaiting the async method can be substituted unless otherwise noted.

EinführungIntroduction

Entitäten können explizit an einen angefügt werden DbContext , sodass der Kontext diese Entitäten nachverfolgt.Entities can be explicitly "attached" to a DbContext such that the context then tracks those entities. Dies ist in erster Linie nützlich, wenn:This is primarily useful when:

  1. Erstellen neuer Entitäten, die in die Datenbank eingefügt werden.Creating new entities that will be inserted into the database.
  2. Erneutes Anfügen von getrennten Entitäten, die zuvor von einer anderen dbcontext-Instanz abgefragt wurden.Re-attaching disconnected entities that were previously queried by a different DbContext instance.

Die ersten werden von den meisten Anwendungen benötigt und werden von den-Methoden als primär behandelt DbContext.Add .The first of these will be needed by most applications, and is primary handled by the DbContext.Add methods.

Die zweite wird nur von Anwendungen benötigt, die Entitäten oder deren Beziehungen ändern, während die Entitäten nicht nachverfolgt werden.The second is only needed by applications that change entities or their relationships while the entities are not being tracked. Beispielsweise kann eine Webanwendung Entitäten an den Webclient senden, an dem der Benutzer Änderungen vornimmt und die Entitäten zurücksendet.For example, a web application may send entities to the web client where the user makes changes and sends the entities back. Diese Entitäten werden als "getrennt" bezeichnet, da Sie ursprünglich von einem dbcontext abgefragt wurden, aber dann beim Senden an den Client von diesem Kontext getrennt wurden.These entities are referred to as "disconnected" since they were originally queried from a DbContext, but were then disconnected from that context when sent to the client.

Die Webanwendung muss nun diese Entitäten erneut anfügen, damit Sie erneut verfolgt werden, und gibt die Änderungen an, die vorgenommen wurden, SaveChanges um geeignete Updates an der Datenbank vornehmen zu können.The web application must now re-attach these entities so that they are again tracked and indicate the changes that have been made such that SaveChanges can make appropriate updates to the database. Dies wird hauptsächlich von der DbContext.Attach -Methode und der- DbContext.Update Methode behandelt.This is primarily handled by the DbContext.Attach and DbContext.Update methods.

Tipp

Das Anfügen von Entitäten an dieselbe dbcontext-Instanz , aus der Sie abgefragt wurden, sollte normalerweise nicht benötigt werden.Attaching entities to the same DbContext instance that they were queried from should not normally be needed. Führen Sie nicht routinemäßig eine Abfrage ohne Nachverfolgung aus, und fügen Sie dann die zurückgegebenen Entitäten demselben Kontext hinzu.Do not routinely perform a no-tracking query and then attach the returned entities to the same context. Dies ist langsamer als die Verwendung einer nach Verfolgungs Abfrage und kann auch zu Problemen führen, wie z. b. fehlende Schatten Eigenschaftswerte, sodass es schwieriger wird, richtig zu werden.This will be slower than using a tracking query, and may also result in issues such as missing shadow property values, making it harder to get right.

Generierte und explizite SchlüsselwerteGenerated versus explicit key values

Standardmäßig sind die Eigenschaften "Integer" und "GUID Key " so konfiguriert, dass automatisch generierte Schlüsselwerteverwendet werden.By default, integer and GUID key properties are configured to use automatically generated key values. Dies hat einen großen Vorteil für die Änderungs Nachverfolgung: ein nicht fest gelegteter Schlüsselwert gibt an, dass die Entität "New" ist.This has a major advantage for change tracking: an unset key value indicates that the entity is "new". "New" bedeutet, dass Sie noch nicht in die Datenbank eingefügt wurde.By "new", we mean that it has not yet been inserted into the database.

In den folgenden Abschnitten werden zwei Modelle verwendet.Two models are used in the following sections. Der erste ist so konfiguriert, dass keine generierten Schlüsselwerte verwendet werden:The first is configured to not use generated key values:

public class Blog
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    public string Title { get; set; }
    public string Content { get; set; }

    public int? BlogId { get; set; }
    public Blog Blog { get; set; }
}

Nicht generierte (d.h. explizit festgelegte) Schlüsselwerte werden in jedem Beispiel zuerst angezeigt, da alles sehr explizit und leicht zu befolgen ist.Non-generated (i.e. explicitly set) key values are shown first in each example because everything is very explicit and easy to follow. Danach folgt ein Beispiel, in dem generierte Schlüsselwerte verwendet werden:This is then followed by an example where generated key values are used:

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int? BlogId { get; set; }
    public Blog Blog { get; set; }
}

Beachten Sie, dass die Schlüsseleigenschaften in diesem Modell hier keine zusätzliche Konfiguration erfordern, da die Verwendung von generierten Schlüsselwerten der Standardwert für einfache ganzzahlige Schlüsselist.Notice that the key properties in this model need no additional configuration here since using generated key values is the default for simple integer keys.

Einfügen von neuen EntitätenInserting new entities

Explizite SchlüsselwerteExplicit key values

Eine Entität muss in dem Added Zustand nachverfolgt werden, der von eingefügt werden soll SaveChanges .An entity must be tracked in the Added state to be inserted by SaveChanges. Entitäten werden in der Regel durch Aufrufen von DbContext.Add , DbContext.AddRange , DbContext.AddAsync , DbContext.AddRangeAsync oder den entsprechenden Methoden für DbSet<TEntity> in den hinzugefügten Zustand versetzt.Entities are typically put in the Added state by calling one of DbContext.Add, DbContext.AddRange, DbContext.AddAsync, DbContext.AddRangeAsync, or the equivalent methods on DbSet<TEntity>.

Tipp

Diese Methoden funktionieren im Kontext der Änderungs Nachverfolgung auf dieselbe Weise.These methods all work in the same way in the context of change tracking. Weitere Informationen finden Sie unter zusätzliche Änderungsnachverfolgung Features .See Additional Change Tracking Features for more information.

So beginnen Sie z. b. mit der Nachverfolgung eines neuen Blogs:For example, to start tracking a new blog:

context.Add(
    new Blog { Id = 1, Name = ".NET Blog", });

Wenn Sie die Debugansicht der Änderungs Nachverfolgung nach diesem-Befehl überprüfen, wird angezeigt, dass der Kontext die neue Entität im Zustand nachverfolgt Added :Inspecting the change tracker debug view following this call shows that the context is tracking the new entity in the Added state:

Blog {Id: 1} Added
  Id: 1 PK
  Name: '.NET Blog'
  Posts: []

Die Add-Methoden funktionieren jedoch nicht nur für eine einzelne Entität.However, the Add methods don't just work on an individual entity. Sie beginnen tatsächlich mit der Nachverfolgung eines voll ständigen Diagramms verwandter Entitäten und versetzen alle in den Added Zustand.They actually start tracking an entire graph of related entities, putting them all to the Added state. Wenn Sie z. b. einen neuen Blog und zugehörige neue Beiträge einfügen möchten:For example, to insert a new blog and associated new posts:

context.Add(
    new Blog
    {
        Id = 1,
        Name = ".NET Blog",
        Posts =
        {
            new Post
            {
                Id = 1,
                Title = "Announcing the Release of EF Core 5.0",
                Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
            },
            new Post
            {
                Id = 2,
                Title = "Announcing F# 5",
                Content = "F# 5 is the latest version of F#, the functional programming language..."
            }
        }
    });

Der Kontext verfolgt nun alle diese Entitäten wie folgt Added :The context is now tracking all these entities as Added:

Blog {Id: 1} Added
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Added
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
Post {Id: 2} Added
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: 1}

Beachten Sie, dass für die Id Schlüsseleigenschaften in den obigen Beispielen explizite Werte festgelegt wurden.Notice that explicit values have been set for the Id key properties in the examples above. Dies liegt daran, dass das Modell hier so konfiguriert wurde, dass explizit festgelegte Schlüsselwerte anstelle von automatisch generierten Schlüsselwerten verwendet werden.This is because the model here has been configured to use explicitly set key values, rather than automatically generated key values. Wenn keine generierten Schlüssel verwendet werden, müssen die Schlüsseleigenschaften explizit festgelegt werden, bevor aufgerufen wird Add .When not using generated keys, the key properties must be explicitly set before calling Add. Diese Schlüsselwerte werden dann eingefügt, wenn SaveChanges aufgerufen wird.These key values are then inserted when SaveChanges is called. Wenn sqlite beispielsweise verwendet wird:For example, when using SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Blogs" ("Id", "Name")
VALUES (@p0, @p1);

-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String), @p3='1' (DbType = String), @p4='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p5='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p2, @p3, @p4, @p5);

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String), @p1='1' (DbType = String), @p2='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p3='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2, @p3);

Alle diese Entitäten werden im Unchanged Zustand nachverfolgt, nachdem SaveChanges abgeschlossen ist, da diese Entitäten nun in der Datenbank vorhanden sind:All of these entities are tracked in the Unchanged state after SaveChanges completes, since these entities now exist in the database:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
Post {Id: 2} Unchanged
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: 1}

Generierte SchlüsselwerteGenerated key values

Wie bereits erwähnt, werden ganzzahlige und GUID- Schlüsseleigenschaften standardmäßig für die Verwendung automatisch generierter Schlüsselwerte konfiguriert.As mentioned above, integer and GUID key properties are configured to use automatically generated key values by default. Dies bedeutet, dass die Anwendung keinen Schlüsselwert explizit festlegen darf.This means that the application must not set any key value explicitly. Wenn Sie z. b. einen neuen Blog einfügen und alle mit generierten Schlüsselwerten Posten möchten:For example, to insert a new blog and posts all with generated key values:

context.Add(
    new Blog
    {
        Name = ".NET Blog",
        Posts =
        {
            new Post
            {
                Title = "Announcing the Release of EF Core 5.0",
                Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
            },
            new Post
            {
                Title = "Announcing F# 5",
                Content = "F# 5 is the latest version of F#, the functional programming language..."
            }
        }
    });

Wie bei expliziten Schlüsselwerten verfolgt der Kontext jetzt alle folgenden Entitäten wie folgt Added :As with explicit key values, the context is now tracking all these entities as Added:

Blog {Id: -2147482644} Added
  Id: -2147482644 PK Temporary
  Name: '.NET Blog'
  Posts: [{Id: -2147482637}, {Id: -2147482636}]
Post {Id: -2147482637} Added
  Id: -2147482637 PK Temporary
  BlogId: -2147482644 FK Temporary
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: -2147482644}
Post {Id: -2147482636} Added
  Id: -2147482636 PK Temporary
  BlogId: -2147482644 FK Temporary
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: -2147482644}

Beachten Sie, dass in diesem Fall temporäre Schlüsselwerte für jede Entität generiert wurden.Notice in this case that temporary key values have been generated for each entity. Diese Werte werden von EF Core verwendet, bis SaveChanges aufgerufen wird, wobei echte Schlüsselwerte aus der Datenbank gelesen werden.These values are used by EF Core until SaveChanges is called, at which point real key values are read back from the database. Wenn sqlite beispielsweise verwendet wird:For example, when using SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Blogs" ("Name")
VALUES (@p0);
SELECT "Id"
FROM "Blogs"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p2='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p3='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p1, @p2, @p3);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Nachdem SaveChanges abgeschlossen ist, wurden alle Entitäten mit ihren tatsächlichen Schlüsselwerten aktualisiert und im Zustand nachverfolgt, Unchanged da Sie nun dem Status in der Datenbank entsprechen:After SaveChanges completes, all of the entities have been updated with their real key values and are tracked in the Unchanged state since they now match the state in the database:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
Post {Id: 2} Unchanged
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: 1}

Dabei handelt es sich genau um denselben Endzustand wie im vorherigen Beispiel, in dem explizite Schlüsselwerte verwendet wurden.This is exactly the same end-state as the previous example that used explicit key values.

Tipp

Ein expliziter Schlüsselwert kann auch dann festgelegt werden, wenn generierte Schlüsselwerte verwendet werden.An explicit key value can still be set even when using generated key values. EF Core wird dann versucht, mit diesem Schlüsselwert einzufügen.EF Core will then attempt to insert using this key value. Einige Daten Bank Konfigurationen, einschließlich SQL Server mit Identitäts Spalten, unterstützen solche Einfügungen nicht und lösen Sieaus (Weitere Informationen findenSie in diesen Dokumenten).Some database configurations, including SQL Server with Identity columns, do not support such inserts and will throw (see these docs for a workaround).

Anhängen vorhandener EntitätenAttaching existing entities

Explizite SchlüsselwerteExplicit key values

Von Abfragen zurückgegebene Entitäten werden im-Zustand nachverfolgt Unchanged .Entities returned from queries are tracked in the Unchanged state. Der Unchanged Status bedeutet, dass die Entität seit dem Abfragen nicht geändert wurde.The Unchanged state means that the entity has not been modified since it was queried. Eine nicht verbundene Entität, die möglicherweise von einem Webclient in einer HTTP-Anforderung zurückgegeben wird, kann in diesen Zustand versetzt werden, entweder mithilfe von DbContext.Attach , DbContext.AttachRange oder den entsprechenden Methoden in DbSet<TEntity> .A disconnected entity, perhaps returned from a web client in an HTTP request, can be put into this state using either DbContext.Attach, DbContext.AttachRange, or the equivalent methods on DbSet<TEntity>. So beginnen Sie z. b. mit der Nachverfolgung eines vorhandenen Blogs:For example, to start tracking an existing blog:

context.Attach(
    new Blog { Id = 1, Name = ".NET Blog", });

Hinweis

In den hier aufgeführten Beispielen werden Entitäten new aus Gründen der Einfachheit explizit erstellt.The examples here are creating entities explicitly with new for simplicity. Normalerweise stammen die Entitäts Instanzen aus einer anderen Quelle, z. b. Wenn Sie von einem Client deserialisiert oder aus Daten in einem HTTP Post erstellt werden.Normally the entity instances will have come from another source, such as being deserialized from a client, or being created from data in an HTTP Post.

Wenn Sie die Debugansicht für die Änderungs Nachverfolgung nach diesem-Befehl überprüfen, wird angezeigt, dass die Entität im- Unchanged ZustandInspecting the change tracker debug view following this call shows that the entity is tracked in the Unchanged state:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: []

Ebenso wie Add Attach legt tatsächlich ein gesamtes Diagramm verbundener Entitäten auf den- Unchanged Zustand fest.Just like Add, Attach actually sets an entire graph of connected entities to the Unchanged state. Um beispielsweise einen vorhandenen Blog und zugehörige vorhandene Beiträge anzufügen:For example, to attach an existing blog and associated existing posts:

context.Attach(
    new Blog
    {
        Id = 1,
        Name = ".NET Blog",
        Posts =
        {
            new Post
            {
                Id = 1,
                Title = "Announcing the Release of EF Core 5.0",
                Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
            },
            new Post
            {
                Id = 2,
                Title = "Announcing F# 5",
                Content = "F# 5 is the latest version of F#, the functional programming language..."
            }
        }
    });

Der Kontext verfolgt nun alle diese Entitäten wie folgt Unchanged :The context is now tracking all these entities as Unchanged:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
Post {Id: 2} Unchanged
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: 1}

Das Aufrufen von SaveChanges an dieser Stelle hat keine Auswirkungen.Calling SaveChanges at this point will have no effect. Alle Entitäten werden als gekennzeichnet Unchanged , sodass keine Aktualisierung in der Datenbank durchführt.All the entities are marked as Unchanged, so there is nothing to update in the database.

Generierte SchlüsselwerteGenerated key values

Wie bereits erwähnt, werden ganzzahlige und GUID- Schlüsseleigenschaften standardmäßig für die Verwendung automatisch generierter Schlüsselwerte konfiguriert.As mentioned above, integer and GUID key properties are configured to use automatically generated key values by default. Dies hat beim Arbeiten mit getrennten Entitäten einen großen Vorteil: ein nicht festgelegter Schlüsselwert gibt an, dass die Entität noch nicht in die Datenbank eingefügt wurde.This has a major advantage when working with disconnected entities: an unset key value indicates that the entity has not yet been inserted into the database. Dies ermöglicht es dem Änderungs nach Verfolgungs, neue Entitäten automatisch zu erkennen und in den Zustand zu versetzen Added .This allows the change tracker to automatically detect new entities and put them in the Added state. Nehmen Sie beispielsweise an, dieses Diagramm eines Blogs und Beiträge anzufügen:For example, consider attaching this graph of a blog and posts:

context.Attach(
    new Blog
    {
        Id = 1,
        Name = ".NET Blog",
        Posts =
        {
            new Post
            {
                Id = 1,
                Title = "Announcing the Release of EF Core 5.0",
                Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
            },
            new Post
            {
                Id = 2,
                Title = "Announcing F# 5",
                Content = "F# 5 is the latest version of F#, the functional programming language..."
            },
            new Post
            {
                Title = "Announcing .NET 5.0",
                Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
            },
        }
    });

Der Blog hat den Schlüsselwert 1, was darauf hinweist, dass er bereits in der Datenbank vorhanden ist.The blog has a key value of 1, indicating that it already exists in the database. Für zwei der Beiträge sind auch Schlüsselwerte festgelegt, das dritte jedoch nicht.Two of the posts also have key values set, but the third does not. EF Core wird dieser Schlüsselwert als 0 angezeigt, der CLR-Standardwert für eine ganze Zahl.EF Core will see this key value as 0, the CLR default for an integer. Dies führt dazu, dass EF Core die neue Entität als Added anstelle von kennzeichnet Unchanged :This results in EF Core marking the new entity as Added instead of Unchanged:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}, {Id: -2147482636}]
Post {Id: -2147482636} Added
  Id: -2147482636 PK Temporary
  BlogId: 1 FK
  Content: '.NET 5.0 includes many enhancements, including single file a...'
  Title: 'Announcing .NET 5.0'
  Blog: {Id: 1}
Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
Post {Id: 2} Unchanged
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'

Das Aufrufen von SaveChanges an dieser Stelle hat keine Auswirkungen auf die Unchanged Entitäten, sondern fügt die neue Entität in die Datenbank ein.Calling SaveChanges at this point does nothing with the Unchanged entities, but inserts the new entity into the database. Wenn sqlite beispielsweise verwendet wird:For example, when using SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Wichtig zu beachten ist, dass EF Core mit generierten Schlüsselwerten automatisch neu von vorhandenen Entitäten in einem getrennten Diagramm unterscheiden kann.The important point to notice here is that, with generated key values, EF Core is able to automatically distinguish new from existing entities in a disconnected graph. Kurz gesagt: Wenn Sie generierte Schlüssel verwenden, wird EF Core immer eine Entität einfügen, wenn für diese Entität kein Schlüsselwert festgelegt ist.In a nutshell, when using generated keys, EF Core will always insert an entity when that entity has no key value set.

Aktualisieren vorhandener EntitätenUpdating existing entities

Explizite SchlüsselwerteExplicit key values

DbContext.Update, DbContext.UpdateRange und die äquivalenten Methoden in DbSet<TEntity> Verhalten sich genau wie die Attach oben beschriebenen Methoden, mit der Ausnahme, dass Entitäten anstelle des-Zustands in den eingefügt werden Modfied Unchanged .DbContext.Update, DbContext.UpdateRange, and the equivalent methods on DbSet<TEntity> behave exactly as the Attach methods described above, except that entities are put into the Modfied instead of the Unchanged state. So beginnen Sie beispielsweise mit der Nachverfolgung eines vorhandenen Blogs wie folgt Modified :For example, to start tracking an existing blog as Modified:

context.Update(
    new Blog { Id = 1, Name = ".NET Blog", });

Wenn Sie die Debugansicht der Änderungs Nachverfolgung nach diesem-Befehl überprüfen, wird angezeigt, dass der Kontext diese Entität im Modified Zustand verfolgt:Inspecting the change tracker debug view following this call shows that the context is tracking this entity in the Modified state:

Blog {Id: 1} Modified
  Id: 1 PK
  Name: '.NET Blog' Modified
  Posts: []

Ebenso wie bei Add und Attach Update kennzeichnet tatsächlich ein gesamtes Diagramm verwandter Entitäten als Modified .Just like with Add and Attach, Update actually marks an entire graph of related entities as Modified. Fügen Sie z. b. einen vorhandenen Blog und zugehörige vorhandene Beiträge wie folgt an Modified :For example, to attach an existing blog and associated existing posts as Modified:

context.Update(
    new Blog
    {
        Id = 1,
        Name = ".NET Blog",
        Posts =
        {
            new Post
            {
                Id = 1,
                Title = "Announcing the Release of EF Core 5.0",
                Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
            },
            new Post
            {
                Id = 2,
                Title = "Announcing F# 5",
                Content = "F# 5 is the latest version of F#, the functional programming language..."
            }
        }
    });

Der Kontext verfolgt nun alle diese Entitäten wie folgt Modified :The context is now tracking all these entities as Modified:

Blog {Id: 1} Modified
  Id: 1 PK
  Name: '.NET Blog' Modified
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
  Id: 1 PK
  BlogId: 1 FK Modified Originally <null>
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
  Title: 'Announcing the Release of EF Core 5.0' Modified
  Blog: {Id: 1}
Post {Id: 2} Modified
  Id: 2 PK
  BlogId: 1 FK Modified Originally <null>
  Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
  Title: 'Announcing F# 5' Modified
  Blog: {Id: 1}

Wenn Sie an diesem Punkt SaveChanges aufrufen, werden Updates für alle diese Entitäten an die Datenbank gesendet.Calling SaveChanges at this point will cause updates to be sent to the database for all these entities. Wenn sqlite beispielsweise verwendet wird:For example, when using SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

Generierte SchlüsselwerteGenerated key values

Wie bei Attach haben generierte Schlüsselwerte denselben größten Vorteil für Update : ein nicht festgelegter Schlüsselwert gibt an, dass die Entität neu ist und noch nicht in die Datenbank eingefügt wurde.As with Attach, generated key values have the same major benefit for Update: an unset key value indicates that the entity is new and has not yet been inserted into the database. Wie bei Attach ermöglicht es dbcontext, neue Entitäten automatisch zu erkennen und Sie in den- Added Zustand zu versetzen.As with Attach, this allows the DbContext to automatically detect new entities and put them in the Added state. Nehmen Sie beispielsweise an, dass Sie Update mit diesem Diagramm eines Blogs und Beiträgen aufrufen:For example, consider calling Update with this graph of a blog and posts:

context.Update(
    new Blog
    {
        Id = 1,
        Name = ".NET Blog",
        Posts =
        {
            new Post
            {
                Id = 1,
                Title = "Announcing the Release of EF Core 5.0",
                Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
            },
            new Post
            {
                Id = 2,
                Title = "Announcing F# 5",
                Content = "F# 5 is the latest version of F#, the functional programming language..."
            },
            new Post
            {
                Title = "Announcing .NET 5.0",
                Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
            },
        }
    });

Wie im Attach Beispiel wird der Post ohne Schlüsselwert als neu erkannt und auf den-Zustand festgelegt Added .As with the Attach example, the post with no key value is detected as new and set to the Added state. Die anderen Entitäten sind wie folgt gekennzeichnet Modified :The other entities are marked as Modified:

Blog {Id: 1} Modified
  Id: 1 PK
  Name: '.NET Blog' Modified
  Posts: [{Id: 1}, {Id: 2}, {Id: -2147482633}]
Post {Id: -2147482633} Added
  Id: -2147482633 PK Temporary
  BlogId: 1 FK
  Content: '.NET 5.0 includes many enhancements, including single file a...'
  Title: 'Announcing .NET 5.0'
  Blog: {Id: 1}
Post {Id: 1} Modified
  Id: 1 PK
  BlogId: 1 FK Modified Originally <null>
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
  Title: 'Announcing the Release of EF Core 5.0' Modified
  Blog: {Id: 1}
Post {Id: 2} Modified
  Id: 2 PK
  BlogId: 1 FK Modified Originally <null>
  Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
  Title: 'Announcing F# 5' Modified
  Blog: {Id: 1}

SaveChangesWenn Sie an dieser Stelle aufrufen, werden Updates für alle vorhandenen Entitäten an die Datenbank gesendet, während die neue Entität eingefügt wird.Calling SaveChanges at this point will cause updates to be sent to the database for all the existing entities, while the new entity is inserted. Wenn sqlite beispielsweise verwendet wird:For example, when using SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Dies ist eine sehr einfache Möglichkeit, um Updates und Einfügungen aus einem getrennten Diagramm zu generieren.This is a very easy way to generate updates and inserts from a disconnected graph. Dies führt jedoch dazu, dass Updates oder Einfügungen für jede Eigenschaft jeder überwachten Entität an die Datenbank gesendet werden, auch wenn einige Eigenschaftswerte möglicherweise nicht geändert wurden.However, it results in updates or inserts being sent to the database for every property of every tracked entity, even when some property values may not have been changed. Das ist nicht zu ängstlich. bei vielen Anwendungen mit kleinen Diagrammen kann dies eine einfache und pragmatische Methode zum Erstellen von Updates sein.Don't be too scared by this; for many applications with small graphs, this can be an easy and pragmatic way of generating updates. Das heißt, dass andere komplexere Muster manchmal zu effizienteren Updates führen können, wie unter Identitäts Auflösung in EF Corebeschrieben.That being said, other more complex patterns can sometimes result in more efficient updates, as described in Identity Resolution in EF Core.

Löschen vorhandener EntitätenDeleting existing entities

Damit eine Entität von SaveChanges gelöscht werden kann, muss Sie im-Zustand nachverfolgt werden Deleted .For an entity to be deleted by SaveChanges it must be tracked in the Deleted state. Entitäten werden in der Regel Deleted durch Aufrufen einer der DbContext.Remove Methoden, DbContext.RemoveRange oder der entsprechenden Methoden in eingefügt DbSet<TEntity> .Entities are typically put in the Deleted state by calling one of DbContext.Remove, DbContext.RemoveRange, or the equivalent methods on DbSet<TEntity>. So markieren Sie z. b. einen vorhandenen Beitrag wie folgt Deleted :For example, to mark an existing post as Deleted:

context.Remove(
    new Post { Id = 2 });

Durch Überprüfen der Debugansicht für die Änderungs Nachverfolgung nach diesem Befehl wird angezeigt, dass der Kontext die Entität im Zustand nachverfolgt Deleted :Inspecting the change tracker debug view following this call shows that the context is tracking the entity in the Deleted state:

Post {Id: 2} Deleted
  Id: 2 PK
  BlogId: <null> FK
  Content: <null>
  Title: <null>
  Blog: <null>

Diese Entität wird gelöscht, wenn SaveChanges aufgerufen wird.This entity will be deleted when SaveChanges is called. Wenn sqlite beispielsweise verwendet wird:For example, when using SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

Nachdem SaveChanges abgeschlossen wurde, wird die gelöschte Entität vom dbcontext getrennt, da Sie nicht mehr in der Datenbank vorhanden ist.After SaveChanges completes, the deleted entity is detached from the DbContext since it no longer exists in the database. Die Debugansicht ist daher leer, da keine Entitäten nachverfolgt werden.The debug view is therefore empty because no entities are being tracked.

Löschen abhängiger/untergeordneter EntitätenDeleting dependent/child entities

Das Löschen abhängiger/untergeordneter Entitäten aus einem Diagramm ist einfacher als das Löschen von Prinzipal-/Unterordnung-Deleting dependent/child entities from a graph is more straightforward than deleting principal/parent entities. Weitere Informationen finden Sie im nächsten Abschnitt und unter Ändern von Fremdschlüsseln und Navigationen .See the next section and Changing Foreign Keys and Navigations for more information.

Es ist ungewöhnlich, für Remove eine Entität aufzurufen, die mit erstellt wurde new .It is unusual to call Remove on an entity created with new. Außerdem Add Attach ist es im Gegensatz Update zu und nicht üblich, Remove für eine Entität aufzurufen, die nicht bereits im-Zustand oder im-Zustand nachverfolgt wird Unchanged Modified .Further, unlike Add, Attach and Update, it is uncommon to call Remove on an entity that isn't already tracked in the Unchanged or Modified state. Stattdessen ist es typisch, eine einzelne Entität oder ein Diagramm verwandter Entitäten zu verfolgen und dann Remove für die Entitäten aufzurufen, die gelöscht werden sollen.Instead it is typical to track a single entity or graph of related entities, and then call Remove on the entities that should be deleted. Dieses Diagramm der nach verfolgten Entitäten wird in der Regel durch Folgendes erstellt:This graph of tracked entities is typically created by either:

  1. Ausführen einer Abfrage für die EntitätenRunning a query for the entities
  2. Verwenden der- Attach Methode oder der- Update Methode in einem Diagramm von getrennten Entitäten, wie in den vorherigen Abschnitten beschrieben.Using the Attach or Update methods on a graph of disconnected entities, as described in the preceding sections.

Beispielsweise ist der Code im vorherigen Abschnitt wahrscheinlicher, dass er einen Beitrag von einem Client erhält, und dann etwa wie folgt vorgehen:For example, the code in the previous section is more likely obtain a post from a client and then do something like this:

context.Attach(post);
context.Remove(post);

Dies verhält sich genau so wie das vorherige Beispiel, da der Aufruf Remove von für eine nicht verfolgte Entität bewirkt, dass Sie zuerst angefügt und dann als markiert wird Deleted .This behaves exactly the same way as the previous example, since calling Remove on an un-tracked entity causes it to first be attached and then marked as Deleted.

In realistischeren Beispielen wird zunächst ein Diagramm mit Entitäten angehängt, und einige dieser Entitäten werden als gelöscht markiert.In more realistic examples, a graph of entities is first attached, and then some of those entities are marked as deleted. Beispiel:For example:

// Attach a blog and associated posts
context.Attach(blog);

// Mark one post as Deleted
context.Remove(blog.Posts[1]);

Alle Entitäten werden als gekennzeichnet Unchanged , mit Ausnahme derjenigen, für die Remove aufgerufen wurde:All entities are marked as Unchanged, except the one on which Remove was called:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
Post {Id: 2} Deleted
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: 1}

Diese Entität wird gelöscht, wenn SaveChanges aufgerufen wird.This entity will be deleted when SaveChanges is called. Wenn sqlite beispielsweise verwendet wird:For example, when using SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

Nachdem SaveChanges abgeschlossen wurde, wird die gelöschte Entität vom dbcontext getrennt, da Sie nicht mehr in der Datenbank vorhanden ist.After SaveChanges completes, the deleted entity is detached from the DbContext since it no longer exists in the database. Andere Entitäten verbleiben im Unchanged Status:Other entities remain in the Unchanged state:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}]
Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}

Löschen von Principal/Parent-EntitätenDeleting principal/parent entities

Jede Beziehung, die zwei Entitäts Typen verbindet, verfügt über ein Prinzipal-oder übergeordnetes Ende und ein abhängiges oder untergeordnetes Ende.Each relationship that connects two entity types has a principal or parent end, and a dependent or child end. Die abhängige/untergeordnete Entität ist die, die die Fremdschlüssel Eigenschaft hat.The dependent/child entity is the one with the foreign key property. In einer 1: n-Beziehung befindet sich der Prinzipal/das übergeordnete Element auf der Seite "1", und das abhängige/untergeordnete Element befindet sich auf der Seite "Many".In a one-to-many relationship, the principal/parent is on the "one" side, and the dependent/child is on the "many" side. Weitere Informationen finden Sie unter Beziehungen .See Relationships for more information.

In den vorangehenden Beispielen haben wir einen Beitrag gelöscht, bei dem es sich um eine abhängige/untergeordnete Entität im Blogbeitrag 1: n-Beziehung handelt.In the preceding examples we were deleting a post, which is a dependent/child entity in the blog-posts one-to-many relationship. Dies ist relativ unkompliziert, da das Entfernen einer abhängigen/untergeordneten Entität keine Auswirkung auf andere Entitäten hat.This is relatively straightforward since removal of a dependent/child entity does not have any impact on other entities. Andererseits muss das Löschen einer Prinzipal-/Parent-Entität auch Auswirkungen auf abhängige/untergeordnete Entitäten haben.On the other hand, deleting a principal/parent entity must also impact any dependent/child entities. Wenn Sie dies nicht tun, würde ein Fremdschlüssel Wert, der auf einen Primärschlüssel Wert verweist, nicht mehr vorhanden sein.Not doing so would leave a foreign key value referencing a primary key value that no longer exists. Dies ist ein ungültiger Modell Status und führt in den meisten Datenbanken zu einem referenziellen Einschränkungs Fehler.This is an invalid model state and results in a referential constraint error in most databases.

Dieser ungültige Modell Zustand kann auf zwei Arten behandelt werden:This invalid model state can be handled in two ways:

  1. Festlegen von FK-Werten auf NULL.Setting FK values to null. Dies gibt an, dass die abhängigen Elemente/untergeordneten Elemente nicht mehr mit einem Prinzipal/übergeordneten Element verknüpft sind.This indicates that the dependents/children are no longer related to any principal/parent. Dies ist die Standardeinstellung für optionale Beziehungen, bei denen der Fremdschlüssel NULL-Werte zulassen muss.This is the default for optional relationships where the foreign key must be nullable. Das Festlegen des FK auf NULL ist für erforderliche Beziehungen ungültig, bei denen der Fremdschlüssel in der Regel keine NULL-Werte zulässt.Setting the FK to null is not valid for required relationships, where the foreign key is typically non-nullable.
  2. Die abhängigen/untergeordneten Elemente werden gelöscht.Deleting the the dependents/children. Dies ist der Standardwert für erforderliche Beziehungen, der auch für optionale Beziehungen gültig ist.This is the default for required relationships, and is also valid for optional relationships.

Ausführliche Informationen zur Änderungs Nachverfolgung und zu Beziehungen finden Sie unter Ändern von Fremdschlüsseln und Navigationen .See Changing Foreign Keys and Navigations for detailed information on change tracking and relationships.

Optionale BeziehungenOptional relationships

Die Post.BlogId Fremdschlüssel Eigenschaft kann im verwendeten Modell auf NULL festgelegt werden.The Post.BlogId foreign key property is nullable in the model we have been using. Dies bedeutet, dass die Beziehung optional ist. Daher besteht das Standardverhalten von EF Core darin, BlogId Fremdschlüssel Eigenschaften auf NULL festzulegen, wenn der Blog gelöscht wird.This means the relationship is optional, and hence the default behavior of EF Core is to set BlogId foreign key properties to null when the blog is deleted. Beispiel:For example:

// Attach a blog and associated posts
context.Attach(blog);

// Mark the blog as deleted
context.Remove(blog);

Wenn Sie die Debugansicht für die Änderungs Nachverfolgung nach dem Aufrufen von untersuchen, Remove wird angezeigt, dass der Blog wie erwartet nun wie erwartet gekennzeichnet ist Deleted :Inspecting the change tracker debug view following the call to Remove shows that, as expected, the blog is now marked as Deleted:

Blog {Id: 1} Deleted
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
  Id: 1 PK
  BlogId: <null> FK Modified Originally 1
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: <null>
Post {Id: 2} Modified
  Id: 2 PK
  BlogId: <null> FK Modified Originally 1
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: <null>

Interessanterweise sind alle zugehörigen Beiträge nun als markiert Modified .More interestingly, all the related posts are now marked as Modified. Dies liegt daran, dass die Fremdschlüssel Eigenschaft in den einzelnen Entitäten auf NULL festgelegt wurde.This is because the foreign key property in each entity has been set to null. Wenn Sie SaveChanges aufrufen, wird der Fremdschlüssel Wert für jeden Post in NULL in der Datenbank aktualisiert, bevor der Blog gelöscht wird:Calling SaveChanges updates the foreign key value for each post to null in the database, before then deleting the blog:

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p1='2' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p2;
SELECT changes();

Nachdem SaveChanges abgeschlossen wurde, wird die gelöschte Entität vom dbcontext getrennt, da Sie nicht mehr in der Datenbank vorhanden ist.After SaveChanges completes, the deleted entity is detached from the DbContext since it no longer exists in the database. Andere Entitäten sind nun als Unchanged mit NULL-Fremdschlüssel Werten gekennzeichnet, die mit dem Status der Datenbank übereinstimmen:Other entities are now marked as Unchanged with null foreign key values, which matches the state of the database:

Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: <null> FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: <null>
Post {Id: 2} Unchanged
  Id: 2 PK
  BlogId: <null> FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: <null>

Erforderliche BeziehungenRequired relationships

Wenn die Post.BlogId Fremdschlüssel Eigenschaft keine NULL-Werte zulässt, wird die Beziehung zwischen Blogs und Beiträgen zu "erforderlich".If the Post.BlogId foreign key property is non-nullable, then the relationship between blogs and posts becomes "required". In dieser Situation löschen EF Core standardmäßig abhängige/untergeordnete Entitäten, wenn der Prinzipal/das übergeordnete Element gelöscht wird.In this situation, EF Core will, by default, delete dependent/child entities when the principal/parent is deleted. Löschen Sie beispielsweise einen Blog mit verwandten Beiträgen wie im vorherigen Beispiel:For example, deleting a blog with related posts as in the previous example:

// Attach a blog and associated posts
context.Attach(blog);

// Mark the blog as deleted
context.Remove(blog);

Wenn Sie die Debugansicht für die Änderungs Nachverfolgung nach dem Aufrufen von untersuchen, Remove wird angezeigt, dass der Blog wie erwartet mit folgendem gekennzeichnet ist Deleted :Inspecting the change tracker debug view following the call to Remove shows that, as expected, the blog is again marked as Deleted:

Blog {Id: 1} Deleted
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Deleted
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
Post {Id: 2} Deleted
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: 1}

Interessanter ist in diesem Fall, dass alle zugehörigen Beiträge auch als gekennzeichnet wurden Deleted .More interestingly in this case is that all related posts have also been marked as Deleted. Das Aufrufen von SaveChanges bewirkt, dass der Blog und alle zugehörigen Beiträge aus der Datenbank gelöscht werden:Calling SaveChanges causes the blog and all related posts to be deleted from the database:

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p1;

Nachdem SaveChanges abgeschlossen ist, werden alle gelöschten Entitäten von dbcontext getrennt, da Sie nicht mehr in der Datenbank vorhanden sind.After SaveChanges completes, all the deleted entities are detached from the DbContext since they no longer exist in the database. Die Ausgabe der Debugansicht ist daher leer.Output from the debug view is therefore empty.

Hinweis

In diesem Dokument wird nur die Oberfläche beim Arbeiten mit Beziehungen in EF Core.This document only scratches the surface on working with relationships in EF Core. Weitere Informationen zum Aktualisieren /löschen abhängiger/untergeordneter Entitäten beim Aufrufen von SaveChanges finden Sie unter Beziehungen .See Relationships for more information on modeling relationships, and Changing Foreign Keys and Navigations for more information on updating/deleting dependent/child entities when calling SaveChanges.

Benutzerdefinierte Nachverfolgung mit trackgraphCustom tracking with TrackGraph

ChangeTracker.TrackGraph funktioniert wie Add , Attach und Update mit der Ausnahme, dass ein Rückruf für jede Entitäts Instanz generiert wird, bevor Sie nachverfolgt wird.ChangeTracker.TrackGraph works like Add, Attach and Update except that it generates a callback for every entity instance before tracking it. Dadurch kann eine benutzerdefinierte Logik verwendet werden, um zu bestimmen, wie einzelne Entitäten in einem Diagramm nachverfolgt werden.This allows custom logic to be used when determining how to track individual entities in a graph.

Nehmen Sie z. b. die Regel EF Core die beim Nachverfolgen von Entitäten mit generierten Schlüsselwerten verwendet: Wenn der Schlüsselwert 0 (null) ist, ist die Entität neu und sollte eingefügt werden.For example, consider the rule EF Core uses when tracking entities with generated key values: if the key value is zero, then the entity is new and should be inserted. Wir erweitern diese Regel, um zu sagen, dass der Schlüsselwert negativ ist, dann sollte die Entität gelöscht werden.Let's extend this rule to say if the key value is negative, then the entity should be deleted. Dies ermöglicht es uns, die Primärschlüssel Werte in Entitäten eines getrennten Diagramms so zu ändern, dass gelöschte Entitäten markiert werden:This allows us to change the primary key values in entities of a disconnected graph to mark deleted entities:

blog.Posts.Add(
    new Post
    {
        Title = "Announcing .NET 5.0",
        Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
    }
);

var toDelete = blog.Posts.Single(e => e.Title == "Announcing F# 5");
toDelete.Id = -toDelete.Id;

Dieses getrennte Diagramm kann dann mithilfe von trackgraph nachverfolgt werden:This disconnected graph can then be tracked using TrackGraph:

public static void UpdateBlog(Blog blog)
{
    using var context = new BlogsContext();

    context.ChangeTracker.TrackGraph(
        blog, node =>
        {
            var propertyEntry = node.Entry.Property("Id");
            var keyValue = (int)propertyEntry.CurrentValue;

            if (keyValue == 0)
            {
                node.Entry.State = EntityState.Added;
            }
            else if (keyValue < 0)
            {
                propertyEntry.CurrentValue = -keyValue;
                node.Entry.State = EntityState.Deleted;
            }
            else
            {
                node.Entry.State = EntityState.Modified;
            }

            Console.WriteLine($"Tracking {node.Entry.Metadata.DisplayName()} with key value {keyValue} as {node.Entry.State}");
        });

    context.SaveChanges();
}

Für jede Entität im Diagramm prüft der obige Code den Primärschlüssel Wert, bevor die Entität nachverfolgt wird.For each entity in the graph, the code above checks the primary key value before tracking the entity. Für nicht festgelegte (0) Schlüsselwerte führt der Code die EF Core normalerweise durch.For unset (zero) key values, the code does what EF Core would normally do. Das heißt, wenn der Schlüssel nicht festgelegt ist, wird die Entität als markiert Added .That is, if the key is not set, then the entity is marked as Added. Wenn der Schlüssel festgelegt ist und der Wert nicht negativ ist, wird die Entität als markiert Modified .If the key is set and the value is non-negative, then the entity is marked as Modified. Wenn jedoch ein negativer Schlüsselwert gefunden wird, wird der tatsächliche, nicht negative Wert wieder hergestellt, und die Entität wird als nachverfolgt Deleted .However, if a negative key value is found, then its real, non-negative value is restored and the entity is tracked as Deleted.

Die Ausgabe aus dem Ausführen dieses Codes lautet wie folgt:The output from running this code is:

Tracking Blog with key value 1 as Modified
Tracking Post with key value 1 as Modified
Tracking Post with key value -2 as Deleted
Tracking Post with key value 0 as Added

Hinweis

Der Einfachheit halber geht dieser Code davon aus, dass jede Entität über eine ganzzahlige Primärschlüssel Id Eigenschaft namens verfügtFor simplicity, this code assumes each entity has an integer primary key property called Id. Dies könnte in eine abstrakte Basisklasse oder Schnittstelle cogeschrieben werden.This could be codified into an abstract base class or interface. Alternativ können die Primärschlüssel Eigenschaft oder die Eigenschaften aus den Metadaten abgerufen werden IEntityType , sodass dieser Code mit jedem beliebigen Entitätstyp funktionieren würde.Alternately, the primary key property or properties could be obtained from the IEntityType metadata such that this code would work with any type of entity.

Trackgraph verfügt über zwei über Ladungen.TrackGraph has two overloads. In der oben verwendeten einfachen Überladung bestimmt EF Core, wann das Durchlaufen des Diagramms beendet werden soll.In the simple overload used above, EF Core determines when to stop traversing the graph. Insbesondere werden neue verknüpfte Entitäten aus einer bestimmten Entität nicht mehr besucht, wenn diese Entität entweder bereits nachverfolgt wurde oder wenn der Rückruf nicht mit der Nachverfolgung der Entität beginnt.Specifically, it stops visiting new related entities from a given entity when that entity is either already tracked, or when the callback does not start tracking the entity.

Die erweiterte Überladung ChangeTracker.TrackGraph<TState>(Object, TState, Func<EntityEntryGraphNode<TState>,Boolean>) verfügt über einen Rückruf, der einen booleschen Wert zurückgibt.The advanced overload, ChangeTracker.TrackGraph<TState>(Object, TState, Func<EntityEntryGraphNode<TState>,Boolean>), has a callback that returns a bool. Wenn der Rückruf false zurückgibt, wird der Diagramm Durchlauf beendet, andernfalls wird der Vorgang fortgesetzt.If the callback returns false, then graph traversal stops, otherwise it continues. Es muss darauf geachtet werden, dass bei Verwendung dieser Überladung unendliche Schleifen vermieden werden.Care must be taken to avoid infinite loops when using this overload.

Die erweiterte Überladung ermöglicht auch das Bereitstellen des Zustands für trackgraph, und dieser Zustand wird dann an jeden Rückruf übergeben.The advanced overload also allows state to be supplied to TrackGraph and this state is then passed to each callback.