update migraciones framework first code entity-framework foreign-keys ef-migrations

entity framework - migraciones - Agregar una clave externa con la migración de Code First



migraciones entity framework code first (5)

Tengo un error al intentar actualizar mi base de datos después de agregar una migración.

Aquí están mis clases antes de agregar migración

public class Product { public Product() { } public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } public bool Istaxable { get; set; } public string DefaultImage { get; set; } public IList<Feature> Features { get; set; } public IList<Descriptor> Descriptors { get; set; } public IList<Image> Images { get; set; } public IList<Category> Categories { get; set; } } public class Feature { public int Id { get; set; } public string Title { get; set; } public string Description { get; set; } }

Ahora quería agregar una clave externa a mi clase de Característica y refactorizar las clases de esta manera:

public class Product { public Product() { } public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } public bool Istaxable { get; set; } public string DefaultImage { get; set; } public IList<Feature> Features { get; set; } public IList<Descriptor> Descriptors { get; set; } public IList<Image> Images { get; set; } public IList<Category> Categories { get; set; } } public class Feature { public int Id { get; set; } public string Title { get; set; } public string Description { get; set; } public string Image { get; set; } public string VideoLink { get; set; } public int ProductId { get; set; } public Product Product { get; set; } }

He añadido una migración con el comando Add-Migration . Agregué un comando Update-Database y aquí está lo que obtuve:

La instrucción ALTER TABLE entró en conflicto con la restricción FOREIGN KEY "FK_dbo.ProductFeatures_dbo.Products_ProductId". El conflicto ocurrió en la base de datos "CBL", tabla "dbo.Products", columna ''ProductId''

¿Qué puedo hacer para resolver este problema y hacer que mis migraciones vuelvan a la normalidad?


Este es un problema antiguo, pero actualmente no es necesario crear una migración por separado y este problema se puede resolver con unos pocos pasos:

  1. Ejecute Add-Migration con los cambios de la entidad (una nueva propiedad de referencia no anulable agregada, ProductId en este caso) para crear una nueva clase de migración
  2. Modificar la migración recién agregada para crear una columna anulable (anulable: verdadero) en lugar de no anulable
  3. Debajo de AddColumn, agregue el comando Sql configurando el valor de la columna según sea necesario
  4. A continuación, agregue el comando AlterColumn que hará que la columna no sea anulable según lo previsto

En el ejemplo anterior esto se vería así:

public override void Up() { AddColumn("dbo.ProductFeatures", "ProductId", c => c.Int(nullable: true)); Sql("UPDATE [dbo].[ProductFeatures] SET ProductId = (SELECT TOP 1 [Id] FROM [dbo].[Products])"); AlterColumn("dbo.ProductFeatures", "ProductId", c => c.Int(nullable: false)); CreateIndex("dbo.ProductFeatures", "ProductId"); AddForeignKey("dbo.ProductFeatures", "ProductId", "dbo.Products", "Id"); }


La clave para resolver este problema es dividir su migración en dos migraciones. Primero, agregue un campo anulable y complete los datos. Segundo, haga del campo una clave foránea requerida.

Primera migración

  1. Agregue la nueva propiedad a su clase como un tipo anulable (por ejemplo, int?)

    public class MyOtherEntity { public int Id { get; set; } } public class MyEntity { ... // New reference to MyOtherEntity public int? MyOtherEntityId { get; set; } ... }

  2. Crear una migración. NOTA: El nombre de la migración no es importante, pero algo como "AddBlogPosts1" lo hace fácil de leer.

    > add-migration AddMyEntityMyOtherEntity1

  3. Esto debería crear una migración que se vea así:

    public partial class AddMyTableNewProperty1 : DbMigration { public override void Up() { AddColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int()); } public override void Down() { DropColumn("dbo.MyEntity", "MyOtherEntityId"); } }

  4. Ahora edite manualmente la migración generada para agregar un valor predeterminado para el nuevo campo. El caso más fácil es cuando el valor predeterminado es invariante. Puede agregar más lógica en el SQL si es necesario. Este ejemplo asumió que todas las instancias de MyEntity apuntan a la misma instancia de MyOtherEntity con ID 1.

    public partial class AddMyTableNewProperty1 : DbMigration { public override void Up() { AddColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int()); // ADD THIS BY HAND Sql(@"UPDATE dbo.MyEntity SET MyOtherEntityId = 1 where MyOtherEntity IS NULL"); } public override void Down() { DropColumn("dbo.MyEntity", "MyOtherEntityId"); } }

  5. Actualizar la base de datos

    > update-database

Segunda migración

  1. Regrese a su clase MyEntity y cambie la nueva propiedad para que represente una clave foránea obligatoria.

    public class MyEntity { ... // Change the int? to int to make it mandatory public int MyOtherEntityId { get; set; } // Create a reference to the other entity public virtual MyOtherEntity MyOtherEntity { get; set; } ... }

  2. Crear otra migración

    > add-migration AddMyEntityMyOtherEntity2

  3. Esto debería crear una migración como la siguiente:

    public partial class AddMyEntityMyOtherEntity2: DbMigration { public override void Up() { AlterColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int(nullable: false)); CreateIndex("dbo.MyEntity", "MyOtherEntityId"); AddForeignKey("dbo.MyEntity", "MyOtherEntityId", "dbo.MyOtherEntity", "Id"); } public override void Down() { DropForeignKey("dbo.MyEntity", "MyOtherEntityId", "dbo.MyOtherEntity"); DropIndex("dbo.MyEntity", new[] { "MyOtherEntityId" }); AlterColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int()); } }

  4. Actualizar la base de datos

    > update-database

Otras notas

  1. Esta técnica funciona para migraciones aplicadas durante el inicio de la aplicación.
  2. Es posible agregar asignaciones más complejas para la nueva columna en el SQL, pero no se ilustra aquí.

La respuesta rápida es: primero agregue una columna anulable para ProductId, luego establezca un valor predeterminado en todas las filas existentes, luego establezca la columna en no nula (si necesita eso) para futuras inserciones, agregue finalmente la restricción de clave externa.

Escribí una publicación larga en el blog sobre esto, con el código fuente completo incluido: http://nodogmablog.bryanhogan.net/2015/04/entity-framework-non-null-foreign-key-migration/


Me encontré con este problema hoy. Así es como lo manejé. Tuve que hacer que mi propiedad FK sea anulable, lo cual no fue un gran problema en mi caso.

Hice mis cambios a mi POCO, generé la migración y luego agregué lo siguiente a la migración.

public partial class LineItemProductionHistory_v4 : DbMigration { public override void Up() { AddColumn("LineItemProductionHistories","TempLineItemId",c=>c.Int()); Sql("Update LineItemProductionHistories Set TempLineItemId = LineItem_Id"); . . Generated code . Sql("Update LineItemProductionHistories Set LineItemId = TempLineItemId"); DropColumn("LineItemProductionHistories", "TempLineItemId"); } }

Solo estoy almacenando temporalmente todas las claves externas, dejando que la migración haga lo que sea para agregar / soltar y luego volver a colocar las claves externas en el FK recién creado.


Puede ocurrir cuando intenta agregar una restricción de clave externa en una columna no anulable de una tabla que ya contiene datos. Si sus tablas contienen datos, intente eliminarlas primero y vuelva a intentar actualizar su base de datos.