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?

475voto

Slauma Punkte 171348

Dadurch, dass Stage erforderlich ist, werden standardmäßig alle Beziehungen eins zu vielen, an denen Stage beteiligt ist, das automatische Löschen aktiviert haben. Das bedeutet, wenn Sie eine Stage-Entität löschen,

  • wird das Löschen direkt auf Side übergehen
  • wird das Löschen direkt auf Card übergehen und da Card und Side eine erforderliche eins-zu-vielen Beziehung haben, bei der standardmäßig das automatische Löschen aktiviert ist, wird es dann erneut von Card auf Side übergehen

Sie haben also zwei automatische Löschpfade von Stage zu Side> - was die Ausnahme verursacht.

`Sie müssen entweder dieStagein mindestens einer der Entitäten optional machen (d.h. das[Erforderlich]-Attribut von denStage`-Eigenschaften entfernen) oder das automatische Löschen mit Fluent API deaktivieren (nicht möglich mit Datenannotationen):

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

modelBuilder.Entity()
    .HasRequired(s => s.Stage)
    .WithMany()
    .WillCascadeOnDelete(false);``

96voto

Cem Mutlu Punkte 1769

Ich hatte eine Tabelle, die eine zirkuläre Beziehung zu anderen hatte, und ich bekam denselben Fehler. Es stellte sich heraus, dass es an dem Fremdschlüssel lag, der nicht null sein durfte. Wenn der Schlüssel nicht null sein darf, muss das zugehörige Objekt gelöscht werden, und zirkuläre Beziehungen erlauben das nicht. Verwenden Sie also einen nullable Fremdschlüssel.

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

51voto

Nexus23 Punkte 5897

Jeder, der sich fragt, wie es in EF Core gemacht wird:

      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.....

27voto

Sean Punkte 13214

Ich habe diesen Fehler bei vielen Entitäten erhalten, als ich von einem EF7-Modell auf eine EF6-Version migriert bin. Ich wollte nicht jedes Element einzeln durchgehen müssen, also habe ich Folgendes verwendet:

builder.Conventions.Remove();
builder.Conventions.Remove();

25voto

Musakkhir Sayyed Punkte 6604

Sie können cascadeDelete auf false oder true setzen (in Ihrer Up() Methode der Migration). Je nach Bedarf.

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

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