wake-up-neo.com

Die Einführung der FOREIGN KEY-Einschränkung kann zu Zyklen oder mehreren Kaskadenpfaden führen - warum?

Ich habe eine Weile damit gerungen und kann nicht recht herausfinden, was passiert. Ich habe eine Karteneinheit, die Seiten enthält (normalerweise 2) - und sowohl Karten als auch Seiten haben eine Bühne. Ich verwende EF Codefirst-Migrationen und die Migrationen schlagen mit diesem Fehler fehl:

Die Einführung der FOREIGN KEY-Einschränkung 'FK_dbo.Sides_dbo.Cards_CardId' in der Tabelle 'Sides' kann zu Zyklen oder mehreren Kaskadenpfaden führen. Geben Sie ON DELETE NO ACTION oder ON UPDATE NO ACTION an oder ändern Sie andere FOREIGN KEY-Einschränkungen.

Hier ist meine Karte Entität:

public class Card
{
    public Card()
    {
        Sides = new Collection<Side>();
        Stage = Stage.ONE;
    }

    [Key]
    [Required]
    public virtual int CardId { get; set; }

    [Required]
    public virtual Stage Stage { get; set; }

    [Required]
    [ForeignKey("CardId")]
    public virtual ICollection<Side> Sides { get; set; }
}

Hier ist meine Side Entität:

public class Side
{
    public Side()
    {
        Stage = Stage.ONE;
    }

    [Key]
    [Required]     
    public virtual int SideId { get; set; } 

    [Required]
    public virtual Stage Stage { get; set; }

    [Required]
    public int CardId { get; set; }

    [ForeignKey("CardId")]
    public virtual Card Card { get; set; }

}

Und hier ist meine Stage Entität:

public class Stage
{
    // Zero
    public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
    // Ten seconds
    public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");

    public static IEnumerable<Stage> Values
    {
        get
        {
            yield return ONE;
            yield return TWO;
        }

    }

    public int StageId { get; set; }
    private readonly TimeSpan span;
    public string Title { get; set; }

    Stage(TimeSpan span, string title)
    {
        this.span = span;
        this.Title = title;
    }

    public TimeSpan Span { get { return span; } }
}

Merkwürdigerweise füge ich meiner Stage-Klasse Folgendes hinzu:

    public int? SideId { get; set; }
    [ForeignKey("SideId")]
    public virtual Side Side { get; set; }

Die Migration wird erfolgreich ausgeführt. Wenn ich SSMS öffne und die Tabellen betrachte, kann ich sehen, dass Stage_StageId zu Cards hinzugefügt wurde (wie erwartet/gewünscht), jedoch enthält Sides keinen Verweis auf Stage (unerwartet).

Wenn ich dann hinzufüge

    [Required]
    [ForeignKey("StageId")]
    public virtual Stage Stage { get; set; }
    public int StageId { get; set; }

In meiner Side-Klasse wird die Spalte StageId meiner Tabelle Side hinzugefügt.

Dies funktioniert, aber in meiner gesamten Anwendung enthält jeder Verweis auf Stage ein SideId, was in einigen Fällen völlig irrelevant ist. Ich möchte meinen Entitäten Card und Side nur eine Stage -Eigenschaft basierend auf der obigen Stage-Klasse zuweisen, ohne die Stage-Klasse nach Möglichkeit mit Referenzeigenschaften zu belasten. ... Was mache ich falsch?

272
SB2055

Da Stageerforderlich ist, ist für alle Eins-zu-Viele-Beziehungen, an denen Stage beteiligt ist, das kaskadierende Löschen standardmäßig aktiviert. Dies bedeutet, dass Sie eine Stage -Entität löschen

  • der Löschvorgang wird direkt zu Side weitergeleitet.
  • der Löschvorgang wird direkt auf Card kaskadiert, und da Card und Side eine erforderliche Eins-zu-Viele-Beziehung haben, wird der Löschvorgang von Card kaskadiert. zu Side

Sie haben also zwei kaskadierende Löschpfade von Stage bis Side - was die Ausnahme verursacht.

Sie müssen entweder das Stage in mindestens einer der Entitäten optional machen (dh das [Required] -Attribut aus den Stage -Eigenschaften entfernen) oder das kaskadierende Löschen mit der Fluent-API deaktivieren (bei Datenanmerkungen nicht möglich) ):

modelBuilder.Entity<Card>()
    .HasRequired(c => c.Stage)
    .WithMany()
    .WillCascadeOnDelete(false);

modelBuilder.Entity<Side>()
    .HasRequired(s => s.Stage)
    .WithMany()
    .WillCascadeOnDelete(false);
346
Slauma

Ich hatte einen Tisch, der eine kreisförmige Beziehung zu anderen hatte, und ich bekam den gleichen Fehler. Es hat sich herausgestellt, dass es sich um den Fremdschlüssel handelt, der nicht nullwertfähig war. Wenn der Schlüssel nicht nullwertfähig ist, muss das zugehörige Objekt gelöscht werden, und zirkuläre Beziehungen lassen dies nicht zu. Verwenden Sie daher einen nullfähigen Fremdschlüssel.

[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }
49
Cem Mutlu

Jeder, der sich fragt, wie es mit EF Core geht:

      protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
                {
                    relationship.DeleteBehavior = DeleteBehavior.Restrict;
                }
           ..... rest of the code.....
28
Nexus23

Ich habe diesen Fehler für viele Entitäten erhalten, als ich von einem EF7-Modell auf eine EF6-Version migriert habe. Ich wollte nicht jedes Objekt einzeln durchgehen müssen, also benutzte ich:

builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
21
Sean

Sie können cascadeDelete auf false oder true setzen (in Ihrer Up () - Migrationsmethode). Hängt von ihrer anforderung.

AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
17

In .NET Core habe ich die Option onDelete in ReferencialAction.NoAction geändert

         constraints: table =>
            {
                table.PrimaryKey("PK_Schedule", x => x.Id);
                table.ForeignKey(
                    name: "FK_Schedule_Teams_HomeId",
                    column: x => x.HomeId,
                    principalTable: "Teams",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.NoAction);
                table.ForeignKey(
                    name: "FK_Schedule_Teams_VisitorId",
                    column: x => x.VisitorId,
                    principalTable: "Teams",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.NoAction);
            });
5
Mike Jones

Ich habe das behoben. Wenn Sie die Migration hinzufügen, wird in der Up () -Methode eine Zeile wie die folgende angezeigt:

.ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)

Wenn Sie nur das cascadeDelete am Ende löschen, funktioniert es.

5
Usman Khan

Ich hatte dieses Problem auch, ich löste es sofort mit diese Antwort von einem ähnlichen Thread

In meinem Fall wollte ich den abhängigen Datensatz beim Löschen des Schlüssels nicht löschen. Wenn dies in Ihrer Situation der Fall ist, ändern Sie einfach den Booleschen Wert bei der Migration in false:

AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);

Es besteht die Möglichkeit, dass Sie Beziehungen erstellen, die diesen Compilerfehler auslösen, die Kaskadenlöschung jedoch beibehalten möchten. Sie haben ein Problem mit Ihren Beziehungen.

5
jonc.js

Nur zu Dokumentationszwecken, für jemanden, der in der Zukunft auftaucht, kann diese Sache so einfach gelöst werden, und mit dieser Methode können Sie eine Methode ausführen, die einmal deaktiviert wurde, und Sie können normal auf Ihre Methode zugreifen

Fügen Sie diese Methode der Kontextdatenbankklasse hinzu:

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
4
sgrysoft

Die vorhandenen Antworten sind großartig. Ich wollte nur hinzufügen, dass ich auf diesen Fehler aus einem anderen Grund gestoßen bin. Ich wollte eine erste EF-Migration für eine vorhandene Datenbank erstellen, habe aber nicht das Flag - IgnoreChanges verwendet und den Befehl Update-Database auf eine leere Datenbank angewendet (auch bei vorhandenen Fehlern).

Stattdessen musste ich diesen Befehl ausführen, wenn die aktuelle Datenbankstruktur die aktuelle ist:

Add-Migration Initial -IgnoreChanges

Es gibt wahrscheinlich ein echtes Problem in der Datenbankstruktur, aber rette die Welt Schritt für Schritt ...

1
CodingYourLife

Das klingt komisch und ich weiß nicht warum, aber in meinem Fall geschah das, weil mein ConnectionString "." im Attribut "Datenquelle". Sobald ich es in "localhost" geändert habe, hat es wie ein Zauber funktioniert. Es war keine weitere Änderung erforderlich.

1
Marco Alves

Keine der oben genannten Lösungen hat bei mir funktioniert. Was ich tun musste, war, ein nullbares int (int?) Für den Fremdschlüssel zu verwenden, das nicht erforderlich war (oder kein Spaltenschlüssel, der nicht null ist), und dann einige meiner Migrationen zu löschen.

Beginnen Sie mit dem Löschen der Migrationen und versuchen Sie es mit dem nullfähigen int.

Problem war sowohl eine Modifikation als auch ein Modellentwurf. Es war keine Codeänderung erforderlich.

0
Ayson Baxter

In . NET Core habe ich mit allen oberen Antworten gespielt - aber ohne Erfolg. Ich habe viele Änderungen an der DB-Struktur vorgenommen und jedes Mal neue Migrationsversuche zu update-database hinzugefügt, aber den gleichen Fehler erhalten.

Dann habe ich nacheinander mit remove-migration begonnen, bis die Package Manager-Konsole eine Ausnahme machte:

Die Migration '20170827183131 _ ***' wurde bereits auf die Datenbank angewendet

Danach habe ich eine neue Migration (add-migration) und update-databaseerfolgreich hinzugefügt

Mein Vorschlag wäre also: Bereinigen Sie alle temporären Migrationen bis zu Ihrem aktuellen DB-Status.

0
rock_walker