3

I want to add a new column with a unique index to an existing table using a code first migration. Because the table already has data, all existing rows are set to NULL for the new column when it's created. The migration fails to apply because this violates the unique constraint of the new index.

Here is my migration:

public override void Up()
{
    AddColumn("dbo.SignInToken", "Token", c => c.String(maxLength: 32));
    // How can I update existing rows here?
    CreateIndex("dbo.SignInToken", "Token", unique: true);
}

And the error:

The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.SignInToken' and the index name 'IX_Token'. The duplicate key value is ().

The new column will contain a randomly generated string which I set in code.

I know that I can use the Sql method to execute raw SQL within the migration. I thought of using a cursor to update each row, but then I would have to write a stored procedure to mimic the C# method which generates the random token. I would prefer not to have to do this. Another thing I thought of was that if there were a way to retrieve all of the rows, I could loop through them and execute the update statements on each row using the Sql method, but I don't know if this is possible.

Is there a way to update all existing rows individually from within the migration before adding the unique constraint?

2 Answers 2

4

One solution is to copy the primary key into the new column. This can then be updated if necessary in the seed method.

Updated migration:

public override void Up()
{
    AddColumn("dbo.SignInToken", "Token", c => c.String(maxLength: 40));
    Sql("UPDATE dbo.SignInToken SET Token = Id");
    CreateIndex("dbo.SignInToken", "Token", unique: true);
}

Then you can use the DbContext from the Seed method to update the rows if necessary. Some logic would be required to make sure it doesn't overwrite new data in future migrations (e.g. only update the row if Id == Token).

Sign up to request clarification or add additional context in comments.

1 Comment

This solution worked well for me. I actually didn't need to bother updating the data in my Seed method, but included that information for others.
0

Also in the case you didn't have a already existing unique column (to copy it's values) you and want to add a unique column to a table that have already some rows you can try this approach

AddColumn("dbo.SignInToken", 
          "Token", 
          c => c.Int(nullable: false,
                     defaultValueSql: "CONVERT(int, CONVERT(VARBINARY(16), NEWID(), 1))"));

CreateIndex("dbo.IriSqlServerLayer", "Order", unique: true, name: "IX_UniqueOrder");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.