Migrazione EF per la modifica del tipo di dati delle colonne

Ho un modello nel mio progetto come di seguito:

public class Model { public int Id { get; set; } public long FromNo { get; set; } public long ToNo { get; set; } public string Content { get; set; } public long TicketNo { get; set; } } 

La migrazione è come sotto

 public override void Down() { AlterColumn("dbo.Received", "FromNo", c => c.Long(nullable: false)); AlterColumn("dbo.Received", "ToNo", c => c.Long(nullable: false)); AlterColumn("dbo.Received", "TicketNo", c => c.Long(nullable: false)); } public override void Up() { AlterColumn("dbo.Received", "FromNo", c => c.String()); AlterColumn("dbo.Received", "ToNo", c => c.String()); AlterColumn("dbo.Received", "TicketNo", c => c.String()); } 

quando uso Update-Database viene sollevato l’errore seguente:

L’object “DF__Receiv__FromN__25869641” dipende dalla colonna “FromNo”. ALTER TABLE ALTER COLUMN FromNo non riuscito perché uno o più oggetti accedono a questa colonna.

Questa tabella non ha chiave esterna o cos’altro qual’è il problema?

Hai un vincolo predefinito sulla colonna. Devi prima eliminare il vincolo, quindi modificare la colonna.

 public override void Up() { Sql("ALTER TABLE dbo.Received DROP CONSTRAINT DF_Receiv_FromN__25869641"); AlterColumn("dbo.Received", "FromNo", c => c.String()); AlterColumn("dbo.Received", "ToNo", c => c.String()); AlterColumn("dbo.Received", "TicketNo", c => c.String()); } 

Probabilmente dovrai abbandonare i vincoli predefiniti anche sulle altre colonne.

Ho appena visto il commento di Andrey (lo so – molto tardi) e ha ragione. Quindi un approccio più robusto sarebbe usare qualcosa come:

  DECLARE @con nvarchar(128) SELECT @con = name FROM sys.default_constraints WHERE parent_object_id = object_id('dbo.Received') AND col_name(parent_object_id, parent_column_id) = 'FromNo'; IF @con IS NOT NULL EXECUTE('ALTER TABLE [dbo].[Received] DROP CONSTRAINT ' + @con) 

So che probabilmente questo non aiuta il PO, ma si spera che aiuti chiunque altro a superare questo problema.

 static internal class MigrationExtensions { public static void DeleteDefaultContraint(this IDbMigration migration, string tableName, string colName, bool suppressTransaction = false) { var sql = new SqlOperation(String.Format(@"DECLARE @SQL varchar(1000) SET @SQL='ALTER TABLE {0} DROP CONSTRAINT ['+(SELECT name FROM sys.default_constraints WHERE parent_object_id = object_id('{0}') AND col_name(parent_object_id, parent_column_id) = '{1}')+']'; PRINT @SQL; EXEC(@SQL);", tableName, colName)) { SuppressTransaction = suppressTransaction }; migration.AddOperation(sql); } } public override void Up() { this.DeleteDefaultContraint("dbo.Received", "FromNo"); AlterColumn("dbo.Received", "FromNo", c => c.String()); this.DeleteDefaultContraint("dbo.Received", "ToNo"); AlterColumn("dbo.Received", "ToNo", c => c.String()); this.DeleteDefaultContraint("dbo.Received", "TicketNo"); AlterColumn("dbo.Received", "TicketNo", c => c.String()); } 

Questo è un esempio per cambiare una colonna esistente in “non null” che ha già un vincolo di chiave esterna. Il nome della colonna è “FKColumnName” nella tabella “SubTable” e fa riferimento alla colonna “Id” nella tabella “MainTable”.

Up script:

Dopo che la colonna è stata resa “not nullable”, l’indice e la chiave esterna sono stati prima eliminati e quindi ricreati.

Down script:

Qui i passaggi sono identici tranne che la colonna viene nuovamente resa nulla.

 public partial class NameOfMigration : DbMigration { public override void Up() { DropForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable"); DropIndex("dbo.SubTable", new[] { "FKColumnName" }); AlterColumn("dbo.SubTable", "FKColumnName", c => c.Int(nullable: false)); CreateIndex("dbo.SubTable", "FKColumnName"); AddForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable", "Id"); } public override void Down() { DropForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable"); DropIndex("dbo.SubTable", new[] { "FKColumnName" }); AlterColumn("dbo.SubTable", "FKColumnName", c => c.Int(nullable: true)); CreateIndex("dbo.SubTable", "FKColumnName"); AddForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable", "Id"); } } 

Il modo migliore è risolvere il problema per sempre.

È ansible implementare una class di generatore sql personalizzata derivata da SqlServerMigrationSqlGenerator dallo spazio dei nomi System.Data.Entity.SqlServer:

 using System.Data.Entity.Migrations.Model; using System.Data.Entity.SqlServer; namespace System.Data.Entity.Migrations.Sql{ internal class FixedSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator { protected override void Generate(AlterColumnOperation alterColumnOperation){ ColumnModel column = alterColumnOperation.Column; var sql = String.Format(@"DECLARE @ConstraintName varchar(1000); DECLARE @sql varchar(1000); SELECT @ConstraintName = name FROM sys.default_constraints WHERE parent_object_id = object_id('{0}') AND col_name(parent_object_id, parent_column_id) = '{1}'; IF(@ConstraintName is NOT Null) BEGIN set @sql='ALTER TABLE {0} DROP CONSTRAINT [' + @ConstraintName+ ']'; exec(@sql); END", alterColumnOperation.Table, column.Name); this.Statement(sql); base.Generate(alterColumnOperation); return; } protected override void Generate(DropColumnOperation dropColumnOperation){ var sql = String.Format(@"DECLARE @SQL varchar(1000) SET @SQL='ALTER TABLE {0} DROP CONSTRAINT [' + (SELECT name FROM sys.default_constraints WHERE parent_object_id = object_id('{0}') AND col_name(parent_object_id, parent_column_id) = '{1}') + ']'; PRINT @SQL; EXEC(@SQL); ", dropColumnOperation.Table, dropColumnOperation.Name); this.Statement(sql); base.Generate(dropColumnOperation); } } } 

e impostare questa configurazione:

 internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = true; SetSqlGenerator("System.Data.SqlClient", new FixedSqlServerMigrationSqlGenerator ()); } ... } 

Stavo riscontrando questo problema con un valore predefinito di zero constraint su una colonna intera.

Nel mio caso l’ho risolto passando da Entity Framework 6.1.x a EF 6.2.0.

C’è un bug noto in EF prima di 6.2 che significa che EF a volte non gestisce questi tipi di vincoli automaticamente quando si alterano le colonne. Quel bug è descritto sul repository Github EF ufficiale qui , Bricelam descrive il problema come:

Quando si aggiungono le colonne NOT NULL, si sintetizza un valore predefinito per qualsiasi riga esistente. Sembra che la nostra logica interrompa i vincoli predefiniti prima che ALTER COLUMN non tenga conto di ciò.

Il commit per la correzione per quel problema può essere trovato qui .

Se stai usando EF:

  • Elimina la cartella di migrazione e il database
  • enable-migrations
  • add-migration initial
  • update-database

Sebbene, questa soluzione rimuova tutti gli articoli correnti nel database. Se questa non è la tua intenzione, ti suggerirei una delle altre risposte.