【问题标题】:Changing column default values in EF5 Code First在 EF5 Code First 中更改列默认值
【发布时间】:2012-08-15 17:52:02
【问题描述】:

我正在尝试使用 CF 为现有数据库构建模型。我有一列忘记设置合理的默认值。而不是通过更改初始迁移的纯度来妥协,我只是想我会创建另一个迁移(这就是迁移的目的,对吧?:)

public override void Up()
{
    AlterColumn("Config", "DefaultTaxPerDollar", c => c.Decimal(nullable: false, precision: 19, scale: 5, defaultValue: 0.087m));
}

public override void Down()
{
    AlterColumn("Config", "DefaultTaxPerDollar", c => c.Decimal(nullable: false, precision: 19, scale: 5, defaultValue: 0.0m));

}

但这会从 SQL Server 产生Column already has a DEFAULT bound to it. 错误。

如何使用 CF 迁移更改默认值?或者,如何简单地删除一个默认值(然后用不同的值重新创建它)?

编辑:

这是生成的 SQL:

ALTER TABLE [Config] ADD CONSTRAINT DF_DefaultTaxPerDollar DEFAULT 0.087 FOR [DefaultTaxPerDollar]
ALTER TABLE [Config] ALTER COLUMN [DefaultTaxPerDollar] [decimal](19, 5) NOT NULL

我想我可能已经找到了一个解决方案,将Sql() 方法与一些受this 帖子启发的复杂SQL 结合使用。问题源于 SQL Server 使用约束来实现默认值(哦!我多么想念 MySQL!)并为约束生成名称。因此,Code First 团队不能简单地更改或删除/重新创建默认值。

【问题讨论】:

  • 能贴出生成的SQL吗?
  • 是的,这就是我的观点。我预计迁移只会添加约束,但不会检查/删除现有的约束。我认为这可以被视为一个错误。
  • 您能提供您的解决方案作为答案吗?
  • 会做 - 一旦我确定它:) 我很确定这会起作用,但由于实际的 SQL 是由 AlterColumnOperation 对象构建的,然后发送到服务器一下子,我的自定义 SQL 必须与所有生成的 SQL 配合得很好,事实证明这有点棘手。当我开始工作时会发布。
  • 仅供参考,这应该在 E6 中修复(参见 Work Item 452

标签: c# entity-framework-5 entity-framework-migrations


【解决方案1】:

删除受 SQL Server 实体框架产生的反向迁移启发的默认约束

    public static void DropDefaultConstraint(string tableName, string columnName, Action<string> executeSQL)
    {
        string constraintVariableName = string.Format("@constraint_{0}", Guid.NewGuid().ToString("N"));

        string sql = string.Format(@"
            DECLARE {0} nvarchar(128)
            SELECT {0} = name
            FROM sys.default_constraints
            WHERE parent_object_id = object_id(N'{1}')
            AND col_name(parent_object_id, parent_column_id) = '{2}';
            IF {0} IS NOT NULL
                EXECUTE('ALTER TABLE {1} DROP CONSTRAINT ' + {0})",
            constraintVariableName,
            tableName,
            columnName);

        executeSQL(sql);
    }

略短,但用法是一样的。

DropDefaultConstraint(TableName, "DefaultTaxPerDollar", q => Sql(q));

Guid 用于创建唯一的变量名称,以防您在一次迁移中删除多个约束。

【讨论】:

  • +1,但 ALTER TABLE [{1}] ... 中的方括号应该被删除。例如,在调用 DropDefaultConstraint("Sales.Orders", ...) 时,它们会导致模式限定表名出现错误。
  • 如果我的约束名包含字符'.',将导致错误。所以我将这一行“EXECUTE('ALTER TABLE {1} DROP CONSTRAINT ' + {0})”改为“ EXECUTE('ALTER TABLE {1} DROP CONSTRAINT [' + {0} + ']')”, " ,它的工作原理。
  • @heavenwing 只是想对您的说明表示感谢,它帮助我在代码中解决了这个问题。
【解决方案2】:

这是一个受this post 启发的解决方案。这不是一个优雅的方法,但它对我有用。


        public static void DropDefaultConstraint(string tableName, string columnName, Action executeSQL)
        {
            // Execute query that drops the UDF that finds the default constraint name
            var query = @"
                    -- recreate UDF 
                    if object_id('[dbo].[GetDefaultConstraintName]') is not null
                    begin 
                        drop function [dbo].[GetDefaultConstraintName]
                    end
                ";
            executeSQL(query);

            // Execute query that (re)creates UDF that finds the default constraint name
            query = @"
                    create function [dbo].[GetDefaultConstraintName] (
                        @TableName varchar(max),
                        @ColumnName varchar(max))
                    returns varchar(max)
                    as
                    begin
                        -- Returns the name of the default constraint for a column

                        declare @Command varchar(max)
                        select
                            @Command = d.name
                        from
                            ((
                            sys.tables t join
                            sys.default_constraints d
                                on
                                    d.parent_object_id = t.object_id) join
                            sys.columns c
                                on
                                    c.object_id = t.object_id and
                                    c.column_id = d.parent_column_id)
                        where
                            t.name = @TableName and
                            c.name = @ColumnName
                        return @Command
                    end
                ";
            executeSQL(query);

            // Execute query that actually drops the constraint
            query = string.Format(@"
                    -- Use UDF to find constraint name
                    DECLARE @Constraint_Name VARCHAR(100)
                    SET @Constraint_Name = [dbo].GetDefaultConstraintName('{0}','{1}')

                    if LEN(@Constraint_Name) > 0 
                    BEGIN
                        DECLARE @query VARCHAR(300)
                        SET @query = 'ALTER TABLE {0} DROP CONSTRAINT ' + @Constraint_Name

                        execute(@query)
                    END", tableName, columnName);
            executeSQL(query);
        }

在你的迁移中,你可以这样称呼它:

DropDefaultConstraint(TableName, "DefaultTaxPerDollar", q => Sql(q));

使用lamba 的原因是您必须对Sql() 进行三个不同的调用。我永远无法让它作为一个长查询工作 - 在许多不同的地方尝试了关键字 GO 的许多组合。我还尝试反转第一个查询的逻辑,以便仅在 UDF 不存在时才重新创建它,并且它不起作用。无论如何,我想每次都重新创建它会更强大。

【讨论】:

    猜你喜欢
    • 2013-01-28
    • 2012-12-11
    • 2013-11-02
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    相关资源
    最近更新 更多