I\'m trying to use CF to build a model for an existing database. I have a column in which I forgot to set a sane default value. And rather than compromise the purity of th
Removal of default constraints inspired by reverse migrations produced by Entity Framework for 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);
}
It's slightly shorter, but the usage is the same.
DropDefaultConstraint(TableName, "DefaultTaxPerDollar", q => Sql(q));
The Guid is used to make a unique variable name in case you are going to drop several constraints in one migration.
Here's a solution that was inspired by this post. It's not exactly an elegant method, but it works for me.
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);
}
And in your migration, you can call it like this:
DropDefaultConstraint(TableName, "DefaultTaxPerDollar", q => Sql(q));
The reason for using the lamba is because you have to make three distinct calls to Sql()
. I was never able to get this to work as one long query - tried many combinations of the keyword GO
in many different places. I also tried reversing the logic on the first query so that the UDF only gets recreated if it does not exist, and it didn't work. I suppose recreating it every time is more robust anyway.