EF迁移以更改列的数据types
我在我的项目中有一个模型如下:
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; } }
迁移如下
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()); }
当我使用更新数据库时,会出现下面的错误:
对象'DF__Receiv__FromN__25869641'依赖于'FromNo'列。 ALTER TABLE ALTER COLUMN FromNo失败,因为一个或多个对象访问此列。
这个表没有外键,还有什么问题呢?
您的列上有一个默认约束。 您需要先删除约束,然后更改您的列。
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()); }
您可能不得不放弃其他列的默认约束。
我刚刚看到安德烈的评论(我知道 – 很晚),他是正确的。 所以更健壮的方法是使用像这样的东西:
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)
我知道这可能不会帮助OP,但希望它可以帮助其他任何人遇到这个问题。
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()); }
这是将现有列更改为“not null”(已有外键约束)的示例。 该列的名称是“SubTable”表中的“FKColumnName”,它引用表“MainTable”中的“Id”列。
上面的脚本:
在该列被设置为“不可为空”之后,索引和外键首先丢弃然后重新创build。
下脚本:
这里的步骤是相同的,只是列再次可以为空。
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"); } }
更好的办法是永远解决问题。
您可以从System.Data.Entity.SqlServer命名空间实现从SqlServerMigrationSqlGenerator派生的自定义SQL生成器类:
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); } } }
并设置此configuration:
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext> { public Configuration() { AutomaticMigrationsEnabled = true; SetSqlGenerator("System.Data.SqlClient", new FixedSqlServerMigrationSqlGenerator ()); } ... }
如果你使用EF:
- 删除迁移文件夹和数据库
-
enable-migrations
-
add-migration initial
-
update-database
尽pipe此解决scheme将删除数据库中的所有当前项目。 如果这不是你的意图,我会build议其他答案之一。