410 Stimmen

Das Hinzufügen einer Fremdschlüssel-Einschränkung kann zu Zyklen oder mehrfachen Kaskadenpfaden führen - warum?

Ich habe eine Weile damit gerungen und kann nicht ganz herausfinden, was passiert. Ich habe eine Card-Entität, die Seiten (normalerweise 2) enthält - und sowohl Cards als auch Seiten haben eine Stage. Ich verwende EF Codefirst-Migrationen und die Migrationen schlagen mit diesem Fehler fehl:

Durch das Einführen des Fremdschlüsselconstraints 'FK_dbo.Sides_dbo.Cards_CardId' in der Tabelle 'Sides' können Zyklen oder mehrere Kaskadenpfade entstehen. Geben Sie ON DELETE NO ACTION oder ON UPDATE NO ACTION an oder ändern Sie andere Fremdschlüssel Constraints.

Hier ist meine Card-Entität:

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

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

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

    [Required]
    [ForeignKey("CardId")]
    public virtual ICollection 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
{
    // Null
    public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
    // Zehn Sekunden
    public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");

    public static IEnumerable 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; } }
}

Was seltsam ist, ist dass, wenn ich Folgendes meiner Stage-Klasse hinzufüge:

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

Die Migration erfolgreich durchgeführt wird. Wenn ich SSMS öffne und mir die Tabellen anschaue, sehe ich, dass Stage_StageId zu Cards hinzugefügt wurde (wie erwartet/gewünscht), jedoch enthält Sides keine Referenz zu Stage (nicht erwartet).

Wenn ich dann hinzufüge

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

Zu meiner Side-Klasse, sehe ich, dass die StageId-Spalte zu meiner Side-Tabelle hinzugefügt wurde.

Dies funktioniert, aber jetzt enthält in meiner Anwendung jede Referenz zu Stage eine SideId, die in einigen Fällen völlig irrelevant ist. Ich möchte meinen Card- und Side-Entitäten einfach eine Stage-Eigenschaft basierend auf obiger Stage-Klasse geben, ohne die Stage-Klasse mit Referenzeigenschaften zu verschmutzen, wenn möglich... was mache ich falsch?

22voto

Mike Jones Punkte 251

In .NET Core habe ich die onDelete-Option auf 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);
            });

12voto

jonc.js Punkte 408

Ich hatte dieses Problem auch, ich habe es sofort mit dieser Antwort aus einem ähnlichen Thread gelöst

In meinem Fall wollte ich den abhängigen Datensatz bei der Schlüssellöschung nicht löschen. Wenn dies auch in Ihrem Fall so ist, ändern Sie einfach den Booleschen Wert in der Migration auf false:

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

Es besteht die Möglichkeit, dass Sie Beziehungen erstellen, die diesen Compilerfehler auslösen, aber trotzdem die Kaskadendöschung beibehalten möchten; dann haben Sie ein Problem mit Ihren Beziehungen.

10voto

Usman Khan Punkte 676

Ich habe das behoben. Wenn Sie die Migration hinzufügen, wird im Up()-Methode eine Zeile wie diese stehen:

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

Wenn Sie einfach das cascadeDelete am Ende löschen, wird es funktionieren.

7voto

sgrysoft Punkte 588

Nur zu Dokumentationszwecken, für jemanden, der in der Zukunft kommt, kann dieses Problem so einfach gelöst werden, und mit dieser Methode könnten Sie eine Methode erstellen, die einmal deaktiviert wurde, und Sie könnten auf Ihre Methode normal zugreifen

Fügen Sie diese Methode der Kontextdatenbankklasse hinzu:

geschützt überschreiben void OnModelCreating(DbModelBuilder modelBuilder) {
    modelBuilder.Conventions.Remove();
}

5voto

rock_walker Punkte 415

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

Dann begann ich, remove-migration einzeln zu entfernen, bis die Package Manager Console eine Ausnahme warf:

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

Nachdem habe ich eine neue Migration hinzugefügt (add-migration) und update-database erfolgreich durchgeführt.

Also wäre mein Vorschlag: Löschen Sie alle temp Migrations, bis zu Ihrem aktuellen DB-Zustand.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X