58

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

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 失败,因为一个或多个对象访问此列。

该表没有外键或其他什么问题?

4

6 回答 6

79

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

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

于 2013-07-27T06:52:19.747 回答
43
static internal class MigrationExtensions
{
    public static void DeleteDefaultConstraint(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.DeleteDefaultConstraint("dbo.Received", "FromNo");
    AlterColumn("dbo.Received", "FromNo", c => c.String());
    this.DeleteDefaultConstraint("dbo.Received", "ToNo");
    AlterColumn("dbo.Received", "ToNo", c => c.String());
    this.DeleteDefaultConstraint("dbo.Received", "TicketNo");
    AlterColumn("dbo.Received", "TicketNo", c => c.String());
}
于 2015-05-08T20:39:14.520 回答
3

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

您可以从 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);
        }
    }
}

并设置此配置:

internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;

        SetSqlGenerator("System.Data.SqlClient", new FixedSqlServerMigrationSqlGenerator ());
    }
    ...
}
于 2017-02-18T11:15:12.860 回答
2

这是将现有列更改为已具有外键约束的“非空”的示例。该列的名称是表“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");
    }
}
于 2016-01-24T13:29:35.553 回答
1

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

就我而言,我通过从 Entity Framework 6.1.x 切换到 EF 6.2.0 解决了这个问题。

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

添加 NOT NULL 列时,我们会为任何现有行合成一个默认值。看起来我们在 ALTER COLUMN 没有考虑到这一点之前删除默认约束的逻辑。

可以在此处找到修复该问题的提交。

于 2018-08-01T01:08:43.810 回答
-16

如果您使用的是 EF:

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

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

于 2016-07-30T19:51:50.333 回答