У меня есть таблица Users для приложения, которое всегда использовало строго Windows Authentication. Из-за этого в нем есть WindowsSid
столбец для SID пользователя его учетной записи. Таблица в том виде, как она есть, создается следующим образом:
CREATE TABLE [dbo].[User](
[UserId] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[WindowsSid] [varbinary](85) NOT NULL,
[UserName] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_User] PRIMARY KEY NONCLUSTERED
(
[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [Unique_User_WindowsSid] UNIQUE NONCLUSTERED
(
[WindowsSid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[User] ADD CONSTRAINT [Default_User_UserID] DEFAULT (newid()) FOR [UserId]
GO
Теперь мы добавляем поддержку Azure Active Directory. Для этого я добавил в таблицу столбец, который будет использовать идентификатор объекта Azure пользователя вместо его SID. Поскольку у этих пользователей не будет SID, мне нужно изменить таблицу, чтобы пустые значения в столбце WindowsSid
. DbContext
теперь выглядит следующим образом:
entity.HasIndex(e => e.WindowsSid)
.HasName("Unique_User_WindowsSid")
.IsUnique();
entity.HasIndex(e => e.AzureAdObjectId)
.HasName("IX_Unique_User_AzureAdObjectId")
.IsUnique();
entity.Property(e => e.WindowsSid)
.IsRequired(false)
.HasMaxLength(85);
Единственное отличие от исходной конфигурации заключается в добавлении уникального индекса AzureObjectId
и изменении IsRequired()
для WindowsSid
на IsRequired(false)
.
Это привело к следующей миграции:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropUniqueConstraint(
name: "Unique_User_WindowsSid",
table: "User");
migrationBuilder.AlterColumn<byte[]>(
name: "WindowsSid",
table: "User",
maxLength: 85,
nullable: true,
oldClrType: typeof(byte[]),
oldMaxLength: 85);
migrationBuilder.AddColumn<Guid>(
name: "AzureAdObjectId",
table: "User",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Unique_User_AzureAdObjectId",
table: "User",
column: "AzureAdObjectId",
unique: true,
filter: "[AzureAdObjectId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "Unique_User_WindowsSid",
table: "User",
column: "WindowsSid",
unique: true,
filter: "[WindowsSid] IS NOT NULL");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Unique_User_AzureAdObjectId",
table: "User");
migrationBuilder.DropIndex(
name: "Unique_User_WindowsSid",
table: "User");
migrationBuilder.DropColumn(
name: "AzureAdObjectId",
table: "User");
migrationBuilder.AlterColumn<byte[]>(
name: "WindowsSid",
table: "User",
maxLength: 85,
nullable: false,
oldClrType: typeof(byte[]),
oldMaxLength: 85,
oldNullable: true);
migrationBuilder.AddUniqueConstraint(
name: "Unique_User_WindowsSid",
table: "User",
column: "WindowsSid");
}
Обновление базы данных таким способом работает нормально, однако у меня возникла проблема при ее откате с использованием метода Down()
. Когда я пытаюсь выполнить откат, я получаю эту ошибку:
Cannot drop the index 'User.Unique_User_WindowsSid', because it does not exist or you do not have permission.
Я исследовал с помощью сценариев фактический SQL, который выполняется при откате, и он выдал следующее:
DROP INDEX [IX_Unique_User_AzureAdObjectId] ON [User];
GO
DROP INDEX [Unique_User_WindowsSid] ON [User];
GO
DECLARE @var0 sysname;
SELECT @var0 = [d].[name]
FROM [sys].[default_constraints] [d]
INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
WHERE ([d].[parent_object_id] = OBJECT_ID(N'[User]') AND [c].[name] = N'AzureAdObjectId');
IF @var0 IS NOT NULL EXEC(N'ALTER TABLE [User] DROP CONSTRAINT [' + @var0 + '];');
ALTER TABLE [User] DROP COLUMN [AzureAdObjectId];
GO
DROP INDEX [Unique_User_WindowsSid] ON [User];
DECLARE @var1 sysname;
SELECT @var1 = [d].[name]
FROM [sys].[default_constraints] [d]
INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
WHERE ([d].[parent_object_id] = OBJECT_ID(N'[User]') AND [c].[name] = N'WindowsSid');
IF @var1 IS NOT NULL EXEC(N'ALTER TABLE [User] DROP CONSTRAINT [' + @var1 + '];');
ALTER TABLE [User] ALTER COLUMN [WindowsSid] varbinary(85) NOT NULL;
CREATE UNIQUE INDEX [Unique_User_WindowsSid] ON [User] ([WindowsSid]);
GO
ALTER TABLE [User] ADD CONSTRAINT [Unique_User_WindowsSid] UNIQUE ([WindowsSid]);
GO
DELETE FROM [__EFMigrationsHistory]
WHERE [MigrationId] = N'20191007182257_MigrationName';
GO
При ближайшем рассмотрении, он работает DROP INDEX [Unique_User_WindowsSid] ON [User]
дважды . Один раз, когда это явно предполагается, и снова при переводе метода AlterColumn()
. Я полагаю, что мог бы пойти и удалить первый DropColumn()
, который был сгенерирован, чтобы заставить его работать, но мне любопытно, что я мог сделать здесь неправильно, чтобы заставить EF Core генерировать откат, который на самом деле не работает.