.net - mvc - La introducción de la restricción FOREIGN KEY puede causar ciclos o varias rutas en cascada, ¿por qué?
entity framework tutorial español (13)
He estado luchando con esto por un tiempo y no puedo entender qué está pasando. Tengo una entidad de Tarjeta que contiene Caras (generalmente 2), y tanto las Tarjetas como las Caras tienen un Escenario. Estoy usando las migraciones de EF Codefirst y las migraciones están fallando con este error:
La introducción de la restricción FOREIGN KEY ''FK_dbo.Sides_dbo.Cards_CardId'' en la tabla ''Lados'' puede provocar ciclos o varias rutas en cascada. Especifique ON DELETE NO ACTION o ON UPDATE NO ACTION, o modifique otras restricciones FOREIGN KEY.
Aquí está mi entidad de tarjeta :
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; }
}
Aquí está mi entidad lateral :
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; }
}
Y aquí está mi entidad de escenario :
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; } }
}
Lo que es extraño es que si agrego lo siguiente a mi clase de escenario:
public int? SideId { get; set; }
[ForeignKey("SideId")]
public virtual Side Side { get; set; }
La migración se ejecuta con éxito. Si abro SSMS y miro las tablas, puedo ver que Stage_StageId
se ha agregado a las Cards
(como se esperaba / deseaba), sin embargo, los Sides
no contienen ninguna referencia a Stage
(no se espera).
Si luego agrego
[Required]
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int StageId { get; set; }
A mi clase Side, veo la columna StageId
agregada a mi tabla Side
.
Esto está funcionando, pero ahora a lo largo de mi aplicación, cualquier referencia a Stage
contiene un SideId
, que en algunos casos es totalmente irrelevante. Me gustaría simplemente darle a mi Card
y entidades Side
una propiedad de Stage
basada en la clase de escenario anterior sin contaminar la clase de escenario con propiedades de referencia, si es posible ... ¿qué estoy haciendo mal?
Cualquiera que se pregunte cómo hacerlo en el núcleo de EF:
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.....
Debido a que se requiere Stage
, todas las relaciones de uno a muchos en las que está involucrado Stage
tendrán habilitada la eliminación en cascada de forma predeterminada. Significa, si eliminas una entidad Stage
- la eliminación se realizará en cascada directamente a
Side
- la eliminación se conectará en cascada directamente a la
Card
y debido a que laCard
y elSide
tienen una relación requerida de uno a muchos con la eliminación en cascada habilitada de nuevo por defecto, luego se conectará en cascada de laCard
alSide
Por lo tanto, tiene dos rutas de eliminación en cascada desde el Stage
al Side
, lo que provoca la excepción.
Debe hacer que el Stage
opcional en al menos una de las entidades (es decir, eliminar el atributo [Required]
de las propiedades del Stage
) o desactivar la eliminación en cascada con Fluent API (no es posible con anotaciones de datos):
modelBuilder.Entity<Card>()
.HasRequired(c => c.Stage)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Side>()
.HasRequired(s => s.Stage)
.WithMany()
.WillCascadeOnDelete(false);
En .NET Core cambié la opción onDelete a ReferencialAction.NoAction
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);
});
En .NET Core jugué con todas las respuestas superiores, pero sin ningún éxito. Hice muchos cambios en la estructura de la base de datos y cada vez añadí una nueva migración intentando update-database
, pero recibí el mismo error.
Luego comencé a remove-migration
una por una hasta que la Consola del Administrador de paquetes me lanzó la excepción:
La migración ''20170827183131 _ ***'' ya se ha aplicado a la base de datos
Después de eso, agregué nueva migración ( add-migration
) y update-database
éxito
Así que mi sugerencia sería: borre todas sus migraciones temporales, hasta su estado de base de datos actual.
Esto suena raro y no sé por qué, pero en mi caso eso sucedió porque mi ConnectionString estaba usando "." en el atributo "fuente de datos". Una vez que lo cambié a "localhost" funcionó a la perfección. No fue necesario ningún otro cambio.
He arreglado esto. Cuando agregue la migración, en el método Up () habrá una línea como esta:
.ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)
Si solo eliminas el cascadeDelete del final, funcionará.
Las respuestas existentes son excelentes. Solo quería agregar que me encontré con este error debido a una razón diferente. Quería crear una migración inicial de EF en una base de datos existente pero no usé el indicador -IgnoreChanges y apliqué el comando Actualizar base de datos en una base de datos vacía (también en los fallos existentes).
En su lugar, tuve que ejecutar este comando cuando la estructura actual de la base de datos es la actual:
Add-Migration Initial -IgnoreChanges
Es probable que haya un problema real en la estructura de la base de datos, pero salve al mundo paso a paso ...
Ninguna de las soluciones mencionadas funcionó para mí. Lo que tuve que hacer fue usar un int (n?) Anulable en la clave externa que no era necesario (o no una clave de columna no nula) y luego eliminar algunas de mis migraciones.
Comience por eliminar las migraciones, luego intente con el int.
El problema era tanto la modificación como el diseño del modelo. No fue necesario cambiar el código.
Puede configurar cascadeDelete en false o true (en su método Up () de migración). Depende de su requerimiento
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
Recibía este error para muchas entidades cuando estaba migrando de un modelo EF7 a una versión EF6. No quería tener que revisar cada entidad de una en una, así que utilicé:
builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Solo para fines de documentación, para alguien que viene en el futuro, esto se puede resolver tan simple como esto, y con este método, podría hacer un método que se deshabilitó una vez, y podría acceder a su método normalmente.
Agregue este método a la clase de base de datos de contexto:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
También tuve este problema, lo resolví instantáneamente con esta respuesta de un hilo similar
En mi caso, no quería eliminar el registro dependiente en la eliminación de clave. Si este es el caso en su situación, simplemente cambie el valor booleano en la migración a falso:
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
Lo más probable es que, si está creando relaciones que arrojan este error del compilador, pero SI quiera mantener la eliminación en cascada; Tienes un problema con tus relaciones.
Tenía una mesa que tenía una relación circular con otros y estaba recibiendo el mismo error. Resulta que se trata de la clave foránea que no era anulable. Si la clave no es anulable, el objeto relacionado debe eliminarse y las relaciones circulares no lo permiten. Por lo tanto, utilice clave externa nulable.
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }