用于更改列的数据类型的EF迁移

时间:2013-07-27 06:20:02

标签: c# visual-studio entity-framework entity-framework-5 ef-migrations

我的项目中有一个模型如下:

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());
}

当我使用Update-Database时,会引发以下错误:

  

对象'DF__Receiv__FromN__25869641'取决于列   'FromNo'。 ALTER TABLE ALTER COLUMN FromNo失败,因为一个或多个   对象访问此列。

这个表没有外键或者还有什么问题?

6 个答案:

答案 0 :(得分:72)

您的列上有默认约束。您需要先删除约束,然后更改列。

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没有帮助,但希望它可以帮助其他任何遇到此问题的人。

答案 1 :(得分:40)

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());
}

答案 2 :(得分:2)

这是将现有列更改为已经具有外键约束的“not null”的示例。 列的名称是表“SubTable”中的“FKColumnName”,它引用表“MainTable”中的“Id”列。

向上脚本:

在使列“不可为空”之后,首先删除索引和外键,然后重新创建。

向下脚本:

这里的步骤是相同的​​,只是列可以再次为空。

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");
    }
}

答案 3 :(得分:2)

更好的方法是永远解决问题。

您可以从System.Data.Entity.SqlServer命名空间实现从SqlServerMigrationSqlGenerator派生的自定义sql生成器类:

<section class="content">
    <div class="row">
        <!-- left column -->
        <div class="col-md-6">
            <!-- general form elements -->
            <div class="box box-primary">
                <div class="box-header with-border">
                    <h3 class="box-title">Live Notification</h3>
                    {{ csrf_field() }}
                </div>
                <!-- /.box-header -->
                <!-- form start -->
                <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
                <script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
                <script src="https://cdn.socket.io/socket.io-1.3.4.js"></script>

                <div class="container">
                    <div class="row">
                        <div class="col-lg-8 col-lg-offset-2" >
                            <div id="messages" ></div>
                        </div>
                    </div>
                </div>
                <script>
                    var socket = io.connect('http://localhost:8000');
                    socket.on('message', function (data) {
                        $("#messages").append("<p>" + data + "</p>");
                    });
                </script>
            </div>
        </div>
    </div>
</section>

并设置此配置:

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);
        }
    }
}

答案 4 :(得分:0)

我遇到了这个问题,整数列的默认值为零约束。

就我而言,我是通过从Entity Framework 6.1.x切换到EF 6.2.0来解决的。

6.2之前的EF中存在一个已知的错误,这意味着EF有时在更改列时不会自动处理这些类型的约束。该错误已在official EF github repo here中进行了描述,Bricelam将问题描述为:

  

添加NOT NULL列时,我们会为   现有行。似乎我们放弃默认约束的逻辑   在ALTER COLUMN之前没有考虑到这一点。

针对该问题can be found here的修复程序的提交。

答案 5 :(得分:-12)

如果你正在使用EF:

  • 删除迁移文件夹和数据库
  • enable-migrations
  • add-migration initial
  • update-database

虽然,此解决方案将删除数据库中的所有当前项目。如果这不是你的意图,我会建议其他一个答案。