Kaskadierendes Delete

Entity Framework Core (EF Core) stellt Beziehungen mithilfe von Fremdschlüsseln dar. Eine Entität mit einem Fremdschlüssel ist die untergeordnete oder abhängige Entität in der Beziehung. Der Fremdschlüsselwert der Entität muss mit dem Primärschlüsselwert (oder einem alternativen Schlüsselwert) des verwandten Prinzipals bzw. der übergeordneten Entität übereinstimmen.

Wenn der Prinzipal bzw. die übergeordnete Entität gelöscht wird, stimmen die Fremdschlüsselwerte der abhängigen/untergeordneten Entitäten nicht mehr mit dem Primärschlüssel oder alternativen Schlüssel eines beliebigen Prinzipals bzw. einer übergeordneten Entität überein. Dabei handelt es sich um einen ungültigen Zustand, der bei den meisten Datenbanken zu einem Verstoß gegen eine referenzielle Einschränkung führt.

Es gibt zwei Möglichkeiten, diesen Verstoß gegen eine referenzielle Einschränkung zu umgehen:

  1. Legen Sie die FK-Werte auf NULL fest.
  2. Löschen Sie auch die abhängigen/untergeordneten Entitäten.

Die erste Option ist nur für optionale Beziehungen zulässig, bei denen die Fremdschlüsseleigenschaft (und die Datenbankspalte, der sie zugeordnet ist) NULL-Werte zulassen muss.

Die zweite Option ist für jede Art von Beziehung zulässig und wird als „kaskadierendes Delete“ bezeichnet.

Tipp

In dieser Dokumentation wird das kaskadierende Delete (und das Löschen verwaister Entitäten) aus der Perspektive von Updates der Datenbank beschrieben. Dabei werden die in Änderungsnachverfolgung in EF Core und Ändern von Fremdschlüsseln und Navigationen vorgestellten Konzepte ausgiebig verwendet. Stellen Sie sicher, dass Sie diese Konzepte vollständig verstehen, bevor Sie sich mit dem Inhalt dieses Artikels beschäftigen.

Tipp

Sie können den gesamten Code in dieser Dokumentation ausführen und debuggen, indem Sie den Beispielcode von GitHub herunterladen.

Wann kaskadierendes Verhalten auftritt

Kaskadierende Deletes sind erforderlich, wenn eine abhängige/untergeordnete Entität nicht mehr dem Prinzipal bzw. der übergeordneten Entität zugeordnet werden kann. Dieser Fall kann auftreten, wenn der Prinzipal bzw. die übergeordnete Entität gelöscht wird oder die abhängige/untergeordnete Entität nicht mehr zugeordnet ist.

Löschen eines Prinzipal bzw. einer übergeordneten Entität

Ziehen Sie dieses einfache Modell in Betracht, bei dem Blog der Prinzipal bzw. die übergeordnete Entität in einer Beziehung mit der abhängigen/untergeordneten Entität Post ist. Post.BlogId ist eine Fremdschlüsseleigenschaft, deren Wert mit dem Primärschlüssel Blog.Id des Blogs übereinstimmen muss, zu dem der Beitrag gehört.

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; }
}

Gemäß der Konventionen wird diese Beziehung als erforderlich konfiguriert, da die Fremdschlüsseleigenschaft Post.BlogId keine NULL-Werte zulässt. Erforderliche Beziehungen werden standardmäßig zur Verwendung von kaskadierenden Deletes konfiguriert. Weitere Informationen zum Modellieren von Beziehungen finden Sie unter Beziehungen.

Beim Löschen eines Blogs werden alle Beiträge durch kaskadierende Deletes gelöscht. Beispiel:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

context.Remove(blog);

context.SaveChanges();

SaveChanges generiert beispielsweise den folgenden SQL-Code mithilfe von SQL Server:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Aufheben einer Beziehung

Anstatt den Blog zu löschen, können Sie auch die Beziehung zwischen jedem Beitrag und dem zugehörigen Blog löschen. Hierzu legen Sie die Referenznavigation Post.Blog für jeden Beitrag auf NULL fest:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

foreach (var post in blog.Posts)
{
    post.Blog = null;
}

context.SaveChanges();

Die Beziehung kann auch durch Entfernen aller Beiträge aus der Sammlungsnavigation Blog.Posts aufgehoben werden:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

blog.Posts.Clear();

context.SaveChanges();

In beiden Fällen ist das Ergebnis identisch: der Blog wird nicht gelöscht, aber die Beiträge werden gelöscht, die keinem Blog mehr zugeordnet sind:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

Das Löschen von Entitäten, die nicht mehr einem Prinzipal bzw. einer abhängigen Entität zugeordnet sind, wird als „Löschen verwaister Entitäten“ bezeichnet.

Tipp

Das kaskadierende Delete und das Löschen verwaister Entitäten sind eng verwandt. Beide führen dazu, dass abhängige/untergeordnete Entitäten gelöscht werden, wenn die Beziehung zum erforderlichen Prinzipal bzw. der übergeordneten Entität getrennt wird. Beim kaskadierenden Delete erfolgt diese Trennung, weil der Prinzipal bzw. die übergeordnete Entität selbst gelöscht wird. Bei verwaisten Entitäten ist der Prinzipal bzw. die übergeordnete Entität weiterhin vorhanden, jedoch besteht die Beziehung zur abhängigen/untergeordneten Entität nicht mehr.

Wo kaskadierendes Verhalten auftritt

Kaskadierendes Verhalten kann in folgenden Szenarios angewendet werden:

  • Entitäten, die durch die aktuelle DbContext-Klasse überwacht werden
  • Entitäten in der Datenbank, die nicht in den Kontext geladen wurden

Kaskadierendes Delete für überwachte Entitäten

EF Core wendet kaskadierendes Verhalten immer auf überwachte Entitäten an. Das bedeutet, dass kaskadierende Verhaltensweisen unabhängig von der Datenbankkonfiguration immer ordnungsgemäß angewendet werden, wenn die Anwendung alle relevanten abhängigen/untergeordneten Entitäten in die DbContext-Klasse lädt (siehe Beispiel oben).

Tipp

Das genaue Timing für die Anwendung kaskadierender Verhaltensweisen auf überwachte Entitäten kann mit ChangeTracker.CascadeDeleteTiming und ChangeTracker.DeleteOrphansTiming gesteuert werden. Weitere Informationen finden Sie unter Ändern von Fremdschlüsseln und Navigationen.

Kaskadierendes Delete in der Datenbank

Viele Datenbanksysteme bieten auch kaskadierende Verhalten, die ausgelöst werden, wenn eine Entität in der Datenbank gelöscht wird. EF Core konfiguriert diese Verhalten anhand der Verhaltensweise des kaskadierenden Deletes im EF Core-Modell, wenn eine Datenbank mit EnsureCreated oder EF Core-Migrationen erstellt wird. Bei Verwendung des obigen Modells wird beispielsweise die folgende Tabelle für Beiträge erstellt, wenn SQL Server verwendet wird:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NULL,
    [Content] nvarchar(max) NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]) ON DELETE CASCADE
);

Beachten Sie, dass die Einschränkung für Fremdschlüssel, die die Beziehung zwischen Blogs und Beiträgen definieren, mit ON DELETE CASCADE konfiguriert ist.

Wenn Sie wissen, dass die Datenbank diese Konfiguration aufweist, können Sie einen Blog löschen, ohne zuerst die Beiträge zu laden. Die Datenbank übernimmt dann das Löschen aller Beiträge, die mit dem Blog verwandt waren. Beispiel:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).First();

context.Remove(blog);

context.SaveChanges();

Beachten Sie, dass es keine Include-Anweisung für Beiträge gibt. Diese werden also nicht geladen. In diesem Fall wird mit SaveChanges nur der Blog gelöscht, da dieser die einzige Entität darstellt, die überwacht wird:

-- Executed DbCommand (6ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

Dies würde zu einer Ausnahme führen, wenn die Fremdschlüsseleinschränkung in der Datenbank nicht für kaskadierende Deletes konfiguriert ist. In diesem Fall werden die Beiträge von der Datenbank gelöscht, da sie bei der Erstellung mit ON DELETE CASCADE konfiguriert wurde.

Hinweis

Datenbanken verfügen normalerweise über keine Möglichkeit zum automatischen Löschen von verwaisten Entitäten. Das liegt daran, dass Datenbanken nur über Fremdschlüssel und über keine Navigationen verfügen, obwohl EF Core Beziehungen mithilfe von Navigationen und Fremdschlüsseln darstellt. Das bedeutet, dass es in der Regel nicht möglich ist, eine Beziehung zu trennen, ohne beide Seiten in die DbContext-Klasse zu laden.

Hinweis

Die In-Memory-Datenbank von EF Core unterstützt kaskadierende Deletes in der Datenbank derzeit nicht.

Warnung

Konfigurieren Sie beim vorläufigen Löschen von Entitäten kein kaskadierendes Delete in der Datenbank. Dies kann dazu führen, dass Entitäten versehentlich tatsächlich gelöscht werden, anstatt nur vorläufig gelöscht zu werden.

Einschränkungen bei kaskadierendem Verhalten in Datenbanken

Einige Datenbanken, insbesondere SQL Server, weisen Einschränkungen bei den kaskadierenden Verhaltensweisen auf, die Zyklen bilden. Betrachten Sie beispielsweise das folgende Modell:

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

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

    public int OwnerId { get; set; }
    public Person Owner { get; set; }
}

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; }

    public int AuthorId { get; set; }
    public Person Author { get; set; }
}

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

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

    public Blog OwnedBlog { get; set; }
}

Dieses Modell umfasst drei Beziehungen, die alle erforderlich sind und daher konventionsbedingt für kaskadierende Deletes konfiguriert sind:

  • Das Löschen eines Blogs führt zu einem kaskadierenden Delete aller zugehöriger Beiträge.
  • Das Löschen des Erstellers von Beiträgen führt zu einem kaskadierenden Delete dieser Beiträge.
  • Das Löschen des Besitzers eines Blogs führt zu einem kaskadierenden Delete des Blogs.

Dieses Verhalten ist sinnvoll (und höchstens streng im Kontext von Blogverwaltungsrichtlinien), jedoch könnte ein Versuch, eine SQL Server-Datenbank mit diesen kaskadierenden Deletes zu erstellen, die folgende Ausnahme auslösen:

Microsoft.Data.SqlClient.SqlException (0x80131904): Das Einführen der FOREIGN KEY-Einschränkung 'FK_Posts_Person_AuthorId' für die 'Posts'-Tabelle kann Schleifen oder mehrere kaskadierende Pfade verursachen. Geben Sie ON DELETE NO ACTION oder ON UPDATE NO ACTION an, oder ändern Sie andere FOREIGN KEY-Einschränkungen.

Es gibt zwei Möglichkeiten, diese Situation zu beheben:

  1. Ändern Sie mindestens eine der Beziehungen so, dass kein kaskadierendes Delete durchgeführt wird.
  2. Konfigurieren Sie die Datenbanken ohne diese kaskadierenden Deletes, und stellen Sie dann sicher, dass alle abhängigen Entitäten geladen werden, damit EF Core das kaskadierende Verhalten durchführen kann.

Wenn Sie beispielsweise den ersten Ansatz im Beispiel anwenden, könnten Sie die Beitrag-Blog-Beziehung als optional einrichten, indem Sie diese mit einer Nullwerte zulassenden Fremdschlüsseleigenschaft versehen:

public int? BlogId { get; set; }

Eine optionale Beziehung ermöglicht, dass der Beitrag ohne Blog vorhanden ist, was bedeutet, dass das kaskadierende Delete nicht mehr standardmäßig konfiguriert ist. Aus diesem Grund besteht der Zyklus aus kaskadierenden Aktionen nicht mehr, und die Datenbank kann ohne Fehler in SQL Server erstellt werden.

Wenn Sie hingegen den zweiten Ansatz anwenden, können Sie die Blogbesitzerbeziehung als erforderlich und mit der Konfiguration für ein kaskadierendes Delete beibehalten, aber die Konfiguration so einrichten, dass sie nur auf überwachte Entitäten, anstelle der gesamten Datenbank angewendet wird:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .HasOne(e => e.Owner)
        .WithOne(e => e.OwnedBlog)
        .OnDelete(DeleteBehavior.ClientCascade);
}

Was geschieht jedoch nun, wenn sowohl eine Person als auch der sich im Besitz dieser Person befindende Blog geladen werden und dann die Person gelöscht wird?

using var context = new BlogsContext();

var owner = context.People.Single(e => e.Name == "ajcvickers");
var blog = context.Blogs.Single(e => e.Owner == owner);

context.Remove(owner);

context.SaveChanges();

EF Core führt ein kaskadierendes Delete für den Besitzer durch, sodass der Blog ebenfalls gelöscht wird:

-- Executed DbCommand (8ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [People]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Wenn der Blog jedoch nicht geladen ist, wenn der Besitzer gelöscht wird:

using var context = new BlogsContext();

var owner = context.People.Single(e => e.Name == "ajcvickers");

context.Remove(owner);

context.SaveChanges();

Dann wird eine Ausnahme aufgrund des Verstoßes gegen die Fremdschlüsseleinschränkung in der Datenbank ausgelöst:

Microsoft.Data.SqlClient.SqlException: Die LÖSCHEN-Anweisung ist mit der REFERENZ-Einschränkung „FK_Blogs_People_OwnerId“ in Konflikt geraten. Der Konflikt ist in der Spalte „OwnerId“ der Tabelle „dbo.Blogs“ in der Datenbank „Scratch“ aufgetreten. Die Anweisung wurde beendet.

Kaskadierende NULL-Werte

Optionale Beziehungen umfassen NULL-Werte zulassende Fremdschlüsseleigenschaften, die NULL-Werte zulassenden Datenbankspalten zugeordnet sind. Das bedeutet, dass der Fremdschlüsselwert auf NULL festgelegt werden kann, wenn der aktuelle Prinzipal bzw. die übergeordnete Entität gelöscht oder von der abhängigen/untergeordneten Entität getrennt wird.

Im Folgenden werden die Beispiele aus dem Abschnitt Wann kaskadierendes Verhalten auftritt noch mal untersucht, jedoch verfügen sie diesmal über eine optionale Beziehung, die von einer NULL-Werte zulassenden Post.BlogId-Fremdschlüsseleigenschaft dargestellt wird:

public int? BlogId { get; set; }

Diese Fremdschlüsseleigenschaft wird für jeden Beitrag auf NULL festgelegt, wenn der zugehörige Blog gelöscht wird. Ein Beispiel hierfür ist der folgende Code, der identisch mit dem vorherigen ist:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

context.Remove(blog);

context.SaveChanges();

Der Code resultiert nun in den folgenden Updates der Datenbank, wenn SaveChanges aufgerufen wird:

-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

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

-- Executed DbCommand (1ms) [Parameters=[@p2='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p2;
SELECT @@ROWCOUNT;

Dies gilt auch, wenn die Beziehung mit einem der oben genannten Beispiele getrennt wird:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

foreach (var post in blog.Posts)
{
    post.Blog = null;
}

context.SaveChanges();

Oder:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

blog.Posts.Clear();

context.SaveChanges();

Dann werden die Beiträge mit NULL-Fremdschlüsselwerten aktualisiert, wenn SaveChanges aufgerufen wird:

-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

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

Weitere Informationen zur EF Core-Verwaltung von Fremdschlüsseln und Navigationen bei Änderungen ihrer Werte finden Sie unter Ändern von Fremdschlüsseln und Navigationen.

Hinweis

Die Behebung von Beziehungen wie dieser stellt seit der ersten Version von Entity Framework in 2008 das Standardverhalten dar. Vor EF Core gab es keinen Namen für dieses Verhalten und es konnte nicht geändert werden. Nun ist es wie im nächsten Abschnitt beschrieben als ClientSetNull bekannt.

Datenbanken können auf diese Weise auch für kaskadierende NULL-Werte konfiguriert werden, wenn ein Prinzipal bzw. eine übergeordnete Entität in einer optionalen Beziehung gelöscht wird. Allerdings ist dieser Fall weitaus seltener als die Verwendung von kaskadierenden Deletes in der Datenbank. Die gleichzeitige Verwendung von kaskadierenden Deletes und kaskadierenden NULL-Werten in der Datenbank führt fast immer zu Beziehungszyklen, wenn Sie SQL Server verwenden. Weitere Informationen zur Konfiguration von kaskadierenden NULL-Werten finden Sie im nächsten Abschnitt.

Konfigurieren von kaskadierendem Verhalten

Tipp

Lesen Sie unbedingt die vorherigen Abschnitte, bevor Sie mit diesem Abschnitt beginnen. Sie werden die hier erläuterten Konfigurationsoptionen vermutlich nicht verstehen, wenn Sie die vorherigen Informationen nicht verinnerlicht haben.

Kaskadierende Verhalten werden mithilfe der OnDelete-Methode in OnModelCreating pro Beziehung konfiguriert. Beispiel:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .HasOne(e => e.Owner)
        .WithOne(e => e.OwnedBlog)
        .OnDelete(DeleteBehavior.ClientCascade);
}

Weitere Informationen zum Konfigurieren von Beziehungen zwischen Entitätstypen finden Sie unter Beziehungen.

OnDelete akzeptiert einen Wert aus der DeleteBehavior-Enumeration (was zugegebenermaßen verwirrend sein kann). Diese Enumeration definiert sowohl das Verhalten von EF Core für überwachte Entitäten und die Konfiguration von kaskadierenden Deletes in der Datenbank, wenn Entity Framework zum Erstellen des Schemas verwendet wird.

Auswirkungen auf das Datenbankschema

In der folgenden Tabelle werden die Ergebnisse der einzelnen OnDelete-Werte für die Fremdschlüsseleinschränkung aufgeführt, die von EF Core-Migrationen oder EnsureCreated erstellt wird.

DeleteBehavior Auswirkungen auf das Datenbankschema
Kaskadieren ON DELETE CASCADE
Einschränken ON DELETE RESTRICT
NoAction Datenbankstandard
SetNull ON DELETE SET NULL
ClientSetNull Datenbankstandard
ClientCascade Datenbankstandard
ClientNoAction Datenbankstandard

Die Verhaltensweisen von ON DELETE NO ACTION (Standardeinstellung der Datenbank) und ON DELETE RESTRICT in relationalen Datenbanken sind in der Regel entweder identisch oder sehr ähnlich. Unabhängig davon, was von NO ACTION impliziert wird, führen beide dieser Optionen zu referenziellen Einschränkungen. Sofern es einen Unterschied gibt, besteht dieser darin, wann die Datenbank die Einschränkungen überprüft. Überprüfen Sie die Dokumentation Ihrer Datenbank auf spezifische Unterschiede zwischen ON DELETE NO ACTION und ON DELETE RESTRICT für Ihr Datenbanksystem.

SQL Server unterstützt ON DELETE RESTRICT nicht, daher wird stattdessen ON DELETE NO ACTION verwendet.

Die einzigen Werte, die kaskadierendes Verhalten in der Datenbank auslösen, sind Cascade und SetNull. Alle anderen Werte konfigurieren die Datenbank so, dass keine kaskadierenden Änderungen auftreten.

Auswirkung auf das Verhalten von SaveChanges

Die Tabellen in den folgenden Abschnitten befassen sich damit, was mit den abhängigen/untergeordneten Entitäten geschieht, wenn der Prinzipal bzw. die übergeordnete Entität gelöscht oder die Beziehung zu den abhängigen/untergeordneten Entitäten getrennt wird. Jede Tabelle umfasst:

  • Optionale (NULL-Werte zulassende Fremdschlüssel) und erforderliche (NULL-Werte nicht zulassende Fremdschlüssel) Beziehungen
  • Wann abhängige/untergeordnete Entitäten geladen und von der DbContext-Klasse überwacht werden, sowie wann sie nur in der Datenbank vorhanden sind

Erforderliche Beziehung mit geladenen abhängigen/untergeordneten Entitäten

DeleteBehavior Bei Löschung des Prinzipals bzw. der übergeordneten Entität Bei Trennung der Beziehung zum Prinzipal bzw. zur übergeordneten Entität
Kaskadieren Abhängige Entitäten werden von EF Core gelöscht Abhängige Entitäten werden von EF Core gelöscht
Einschränken InvalidOperationException InvalidOperationException
NoAction InvalidOperationException InvalidOperationException
SetNull SqlException bei Erstellung der Datenbank SqlException bei Erstellung der Datenbank
ClientSetNull InvalidOperationException InvalidOperationException
ClientCascade Abhängige Entitäten werden von EF Core gelöscht Abhängige Entitäten werden von EF Core gelöscht
ClientNoAction DbUpdateException InvalidOperationException

Hinweise:

  • Der Standardwert für erforderliche Beziehungen wie diese lautet: Cascade.
  • Die Verwendung anderer Optionen als das kaskadierende Delete für erforderliche Beziehungen führt zu einer Ausnahme, wenn SaveChanges aufgerufen wird.
    • Hierbei handelt es sich in der Regel um eine InvalidOperationException-Ausnahme von EF Core, da der ungültige Zustand in den geladenen untergeordneten/abhängigen Entitäten erkannt wird.
    • ClientNoAction zwingt EF Core dazu, abhängige Behebungen zu überprüfen, bevor sie an die Datenbank übermittelt werden. Daher löst die Datenbank in diesem Fall eine Ausnahme aus, die dann von SaveChanges mit einer DbUpdateException-Ausnahme umschlossen wird.
    • SetNull wird beim Erstellen der Datenbank nicht zugelassen, da die Fremdschlüsselspalte keine NULL-Werte zulässt.
  • Da abhängige/untergeordnete Entitäten geladen werden, werden sie immer von EF Core gelöscht. Sie werden nie von der Datenbank gelöscht.

Erforderliche Beziehung mit nicht geladenen abhängigen/untergeordneten Entitäten

DeleteBehavior Bei Löschung des Prinzipals bzw. der übergeordneten Entität Bei Trennung der Beziehung zum Prinzipal bzw. zur übergeordneten Entität
Kaskadieren Abhängige Entitäten werden von der Datenbank gelöscht Nicht zutreffend
Einschränken DbUpdateException Nicht zutreffend
NoAction DbUpdateException Nicht zutreffend
SetNull SqlException bei Erstellung der Datenbank Nicht zutreffend
ClientSetNull DbUpdateException Nicht zutreffend
ClientCascade DbUpdateException Nicht zutreffend
ClientNoAction DbUpdateException Nicht zutreffend

Hinweise:

  • Das Trennen einer Beziehung ist hier nicht zulässig, da die abhängigen/untergeordneten Entitäten nicht geladen werden.
  • Der Standardwert für erforderliche Beziehungen wie diese lautet: Cascade.
  • Die Verwendung anderer Optionen als das kaskadierende Delete für erforderliche Beziehungen führt zu einer Ausnahme, wenn SaveChanges aufgerufen wird.
    • In der Regel handelt es sich dabei um eine DbUpdateException-Ausnahme, da die abhängigen/untergeordneten Entitäten nicht geladen werden. Aus diesem Grund kann der ungültige Zustand nur von der Datenbank erkannt werden. Anschließend umschließt SaveChanges die Datenbankausnahme in DbUpdateException.
    • SetNull wird beim Erstellen der Datenbank nicht zugelassen, da die Fremdschlüsselspalte keine NULL-Werte zulässt.

Optionale Beziehung mit geladenen abhängigen/untergeordneten Entitäten

DeleteBehavior Bei Löschung des Prinzipals bzw. der übergeordneten Entität Bei Trennung der Beziehung zum Prinzipal bzw. zur übergeordneten Entität
Kaskadieren Abhängige Entitäten werden von EF Core gelöscht Abhängige Entitäten werden von EF Core gelöscht
Einschränken Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt
NoAction Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt
SetNull Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt
ClientSetNull Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt
ClientCascade Abhängige Entitäten werden von EF Core gelöscht Abhängige Entitäten werden von EF Core gelöscht
ClientNoAction DbUpdateException Abhängige Fremdschlüssel werden von EF Core auf NULL festgelegt

Hinweise:

  • Der Standardwert für optionale Beziehungen wie diese lautet: ClientSetNull.
  • Abhängige/untergeordnete Entitäten werden nie gelöscht, es sei denn, Cascade oder ClientCascade ist konfiguriert.
  • Alle anderen Werte führen dazu, dass die abhängigen Fremdschlüssel von EF Core auf NULL festgelegt werden.
    • Eine Ausnahme hierfür ist der Wert ClientNoAction, der EF Core anweist, keine Änderungen an den Fremdschlüsseln von abhängigen/untergeordneten Entitäten vorzunehmen, wenn das Prinzipal bzw. die übergeordnete Entität gelöscht wird. Die Datenbank löst daher eine Ausnahme aus, die von SaveChanges in DbUpdateException umschlossen wird.

Optionale Beziehung mit nicht geladenen abhängigen/untergeordneten Entitäten

DeleteBehavior Bei Löschung des Prinzipals bzw. der übergeordneten Entität Bei Trennung der Beziehung zum Prinzipal bzw. zur übergeordneten Entität
Kaskadieren Abhängige Entitäten werden von der Datenbank gelöscht Nicht zutreffend
Einschränken DbUpdateException Nicht zutreffend
NoAction DbUpdateException Nicht zutreffend
SetNull Abhängige Fremdschlüssel werden von der Datenbank auf NULL festgelegt Nicht zutreffend
ClientSetNull DbUpdateException Nicht zutreffend
ClientCascade DbUpdateException Nicht zutreffend
ClientNoAction DbUpdateException Nicht zutreffend

Hinweise:

  • Das Trennen einer Beziehung ist hier nicht zulässig, da die abhängigen/untergeordneten Entitäten nicht geladen werden.
  • Der Standardwert für optionale Beziehungen wie diese lautet: ClientSetNull.
  • Abhängige/untergeordnete Entitäten müssen geladen werden, um eine Datenbankausnahme zu vermeiden, es sei denn, die Datenbank wurde für kaskadierende Deletes oder NULL-Werte konfiguriert.