【问题标题】:How to migrate Identity users from a MVC5 app to a ASP.NET Core 2.2 app如何将身份用户从 MVC5 应用程序迁移到 ASP.NET Core 2.2 应用程序
【发布时间】:2019-05-21 12:36:48
【问题描述】:

我有一个使用 Identity 在 MVC5 上构建的 Web 应用程序。我想将此项目转换为 ASP.NET Core 2.2 Web 应用程序。我创建了一个新的 ASP.NET Core 2.2 Web 应用程序,并将身份验证设置为个人用户帐户,并通过关注 these instructions 将我的数据库添加到项目中。然后,我在我的项目中添加了一个新的 Identity Scaffolded 项目,并添加了迁移并更新了数据库。

我注册了一个测试用户,当我检查 SQL 管理服务器时,我看到它为这个项目创建了一个新数据库,即使我的连接字符串是用于我的旧数据库的。

我想保留我的旧数据库,但将其转换为使用 ASP.NET Core 内置的新 Identity Razor 页面。这样做的最佳方法是什么?

【问题讨论】:

标签: c# .net asp.net-mvc asp.net-core


【解决方案1】:

升级身份表后,您可能希望更新现有用户密码哈希。 AspNetUsers 表中的一些新列将具有 NULL 值。首先运行这个:

UPDATE AspNetUsers SET NormalizedEmail = UPPER(Email), NormalizedUserName = UPPER(UserName)
WHERE NormalizedEmail IS NULL

我们需要一种方法来区分哪些用户正在使用新的哈希版本。

一种方法是向 IdentityUser 添加一个新属性:

public class ApplicationUser : IdentityUser
{
    public PasswordHashVersion HashVersion { get; set; }

    public ApplicationUser()
    {
        this.HashVersion = PasswordHashVersion.Core;
    }
}

public enum PasswordHashVersion
{
    OldMvc,
    Core
}

现有用户将默认PasswordHashVersion 等于零(OldMvc),新注册用户将默认为一(核心)。如果您有更智能的方法来检测哈希是来自新算法还是旧算法,则不需要它。

然后我们创建一个自定义 PasswordHash,它使用old default hash algorithm implementation:

public class OldMvcPasswordHasher : PasswordHasher<ApplicationUser>
{
    public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
    {
        // if it's the new algorithm version, delegate the call to parent class
        if (user.HashVersion == PasswordHashVersion.Core)
            return base.VerifyHashedPassword(user, hashedPassword, providedPassword);

        byte[] buffer4;
        if (hashedPassword == null)
        {
            return PasswordVerificationResult.Failed;
        }
        if (providedPassword == null)
        {
            throw new ArgumentNullException("providedPassword");
        }
        byte[] src = Convert.FromBase64String(hashedPassword);
        if ((src.Length != 0x31) || (src[0] != 0))
        {
            return PasswordVerificationResult.Failed;
        }
        byte[] dst = new byte[0x10];
        Buffer.BlockCopy(src, 1, dst, 0, 0x10);
        byte[] buffer3 = new byte[0x20];
        Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
        using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(providedPassword, dst, 0x3e8))
        {
            buffer4 = bytes.GetBytes(0x20);
        }
        if (AreHashesEqual(buffer3, buffer4))
        {
            user.HashVersion = PasswordHashVersion.Core;
            return PasswordVerificationResult.SuccessRehashNeeded;
        }
        return PasswordVerificationResult.Failed;
    }

    private bool AreHashesEqual(byte[] firstHash, byte[] secondHash)
    {
        int _minHashLength = firstHash.Length <= secondHash.Length ? firstHash.Length : secondHash.Length;
        var xor = firstHash.Length ^ secondHash.Length;
        for (int i = 0; i < _minHashLength; i++)
            xor |= firstHash[i] ^ secondHash[i];
        return 0 == xor;
    }
}

这个类继承了新的身份核心PasswordHasher。如果用户的密码哈希版本已经在使用新算法(例如 HashVersion = Core),那么我们只需调用使用新算法的PasswordHasher 的基本方法。否则,使用旧的身份算法验证密码。

如果密码匹配,我们将用户密码哈希版本更新为Core,并返回PasswordVerificationResult.SuccessRehashNeeded以强制使用新算法更新现有哈希。

最后,您需要确保正在使用您的自定义 PasswordHasher。将此添加到Startup.cs 内的ConfigureServices

// Replace the existing scoped IPasswordHasher<> implementation
services.Replace(new ServiceDescriptor(
    serviceType: typeof(IPasswordHasher<ApplicationUser>),
    implementationType: typeof(OldMvcPasswordHasher),
    ServiceLifetime.Scoped));

这必须在对AddIdentityAddDefaultIdentityAddIdentityCore 的任何调用之后添加。

这将在您的用户进行身份验证时慢慢更新密码哈希。

【讨论】:

  • 在我们的例子中,场景是新旧项目同时使用,所以我们需要像以前一样对密码进行哈希处理。这个答案的一个小改动解决了我们的问题。我们只是删除了与检测哈希版本和更新密码哈希相关的部分。
  • 这里与@vahid 相同。我们所做的基本上是在 .net 核心版本中保持哈希不变,因此该数据库可用于对旧 asp mvc 应用程序的用户进行身份验证。
  • 我们正在从 Framework 4.7.2 上的 MVC 项目更新到 .NET 5.0,对我们来说,我们只是复制了现有表,但随后不得不更新 AspNetUsers 上的空字段(标准化用户名等)桌子。然后在 startup.cs 中放入兼容性标志: services.Configure(options => options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2);
【解决方案2】:

我刚刚能够通过以下步骤成功地将 .NET 4.5.2 项目迁移到 .NET Core 3.1

  1. 使用Scaffold-DbContext 基于现有数据库[1] 创建模型
Scaffold-DbContext [-Connection] [-Provider] [-OutputDir] [-Context] [-Schemas>] [-Tables>] [-DataAnnotations] [-Force] [-Project] [-StartupProject] [<CommonParameters>]
  1. 从生成的上下文中删除所有与 AspNet 相关的表及其不再需要的 .cs 文件。

  2. base.OnModelCreating(modelBuilder); 添加到上下文文件中生成的OnModelCreating 方法中。 [2]

  3. 运行下面的脚本来更新/创建身份表[3][4]

ALTER TABLE ASPNETROLES
ADD
 ConcurrencyStamp VARCHAR(255) NULL,               
 NormalizedName VARCHAR(255) NULL

 DROP TABLE AspNetUserTokens

 CREATE TABLE [AspNetUserTokens] (
    [UserId]        NVARCHAR (450) NOT NULL,
    [LoginProvider] NVARCHAR (450) NOT NULL,
    [Name]          NVARCHAR (450) NOT NULL,
    [Value]         NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_AspNetUserTokens]
 PRIMARY KEY CLUSTERED ([UserId] ASC, [LoginProvider] ASC, [Name] ASC)
)

ALTER TABLE AspNetUsers
 ADD
 ConcurrencyStamp VARCHAR(255) NULL,
 LockoutEnd DATETIME NULL,
 NormalizedEmail VARCHAR(255) NULL,
 NormalizedUserName VARCHAR(255) NULL

DROP TABLE [AspNetRoleClaims]

CREATE TABLE [AspNetRoleClaims] (
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [ClaimType]  NVARCHAR (MAX) NULL,
    [ClaimValue] NVARCHAR (MAX) NULL,
    [RoleId]     NVARCHAR (128) NOT NULL,
    CONSTRAINT [PK_AspNetRoleClaims]
 PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId]
 FOREIGN KEY ([RoleId])
  REFERENCES [dbo].[AspNetRoles] ([Id]) ON DELETE CASCADE
)


GO
CREATE NONCLUSTERED INDEX [IX_AspNetRoleClaims_RoleId]
    ON [AspNetRoleClaims]([RoleId] ASC)

ALTER TABLE AspNetUserLogins
   ADD ProviderDisplayName VARCHAR(255) NULL

UPDATE AspNetUsers SET NormalizedEmail = UPPER(Email), NormalizedUserName = UPPER(UserName)
WHERE NormalizedEmail IS NULL
  1. Startup.cs 文件中设置密码哈希兼容模式以考虑 IdentityV2
services.Configure<PasswordHasherOptions>(options => options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2);

【讨论】:

    【解决方案3】:

    另一种解决方案是将数据导出/导入到新表中。这是我出于几个不同的原因所做的,但最终不想更改我的原始源表,以防我需要它快速而轻松地恢复。

    长话短说,您可以将旧数据直接导入/导出到新的身份表,另外唯一需要的是规范化的用户名和电子邮件值。

    如果 Identity 能够在用户首次登录时毫无问题地处理(更新)新密码哈希、安全标记和并发标记时在内部进行处理,那就太好了。

    我使用完整密码迁移用户的超级快捷方式:

    1) 将旧版本 Identity 上的用户表导出到 CSV 文件。

    2) 在 Excel 中打开该 csv 文件,并在 CSV 文件中添加三列,用于缺少两个规范化列(用户名和电子邮件)和一个缺少的并发标记列(并发列并不是真正需要的)。

    3) 在第一个空白规范化列(通常是 c2)中,添加 Excel 公式 =UPPER(b2)(其中 b2 将是您尝试规范化的第一个值的字段。 . 例如,直接导出时的 Username=b2 或 Email=d2,您将规范化列放在其非规范化对应项旁边)。点击回车,您现在应该会在字段中看到大写的值。

    4) 将该公式向下拖动到电子表格的末尾,将所有用户名和电子邮件规范化为其大写值(即全部大写 = 规范化)。

    5) 从电子表格中复制大写值并将它们粘贴到一个空的记事本文件中...然后将它们复制回电子表格中的相同位置(这会用标准化的公式覆盖公式值。Excel 可能足够聪明,可以摆脱公式并将标准化值保存在 CSV 文件中,但我没有对其进行测试。

    6) 将电子表格另存为 CSV,并将其导入到新的身份 AspnetUsers 表中。

    并发标记可以留空,一旦用户第一次登录,最新的身份将处理所有其他事情。

    如果您尚未在新版本中启用该功能并在旧版本中启用,Identity 也不会登录用户名(因此您需要启用该功能或​​将您的用户名转换为相关的电子邮件地址,然后才能成功登录)。

    这并不漂亮,但对于一次性工作,我用这种方法将我的用户从 Identity 4.0.30319 迁移到 Core 2.2 不到 15 分钟,并且不需要更改密码。与迁移用户相比,我输入所有这些所需的时间更长。希望对您有所帮助。

    【讨论】:

    • 谢谢...尤其是这一行:“长话短说,您可以将旧数据直接导入/导出到新的身份表中,唯一需要的是标准化的用户名和电子邮件值。”
    • 我有使用社交媒体帐户登录的用户,这种方法对他们有用吗?还是因为还涉及“提供者”表,所以还有更多工作要做。
    • @rickthehat 已经有一段时间了,但它应该可以工作。我们有拥有社交媒体账户的用户。如果您更改域名或类似的东西(很难记住),则可能需要在社交媒体方面采取一些行动。当我这样做时,社交媒体提供商被保存在 UserLogins 表中,它只是该表上的直接导出 => 导入。不幸的是,我的主要开发笔记本电脑死了,所以我目前无法建立一个项目来测试它,看看可能发生了什么变化。但是,基本上只是将旧数据库模仿到新数据库,你应该会很好。
    【解决方案4】:

    我已经编写了迁移到 AspNetCore Identity 的 SQL 脚本。

    此脚本与 AspNet Identity完全向后兼容,因为我已向表中添加了新列并扩展了已存在的列。

    让我们看一下通过 Scheme compare 生成的屏幕截图:

    您可以在下面看到最终脚本

    BEGIN TRANSACTION;
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SET XACT_ABORT ON;
    
    GO
    PRINT N'Starting dropping constraints';
    
    GO
    ALTER TABLE [dbo].[AspNetUserRoles]
        DROP CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId];
    
    GO
    ALTER TABLE [dbo].[AspNetUserRoles]
        DROP CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId];
    
    GO
    ALTER TABLE [dbo].[AspNetUserClaims]
        DROP CONSTRAINT [FK_dbo.AspNetUserClaims_dbo.AspNetUsers_UserId];
    
    GO
    ALTER TABLE [dbo].[AspNetUserLogins]
        DROP CONSTRAINT [FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId];
    
    -----------------------------------------------------------------------------
    GO
    PRINT N'Starting rebuilding table [dbo].[AspNetRoles]...';
    
    GO
    BEGIN TRANSACTION;
        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
        SET XACT_ABORT ON;
    
        CREATE TABLE [dbo].[tmp_ms_xx_AspNetRoles] (
            [Id]               NVARCHAR (450) NOT NULL,
            [Name]             NVARCHAR (256) NULL,
            [NormalizedName]   NVARCHAR (256) NULL,
            [ConcurrencyStamp] NVARCHAR (MAX) NULL,
            CONSTRAINT [tmp_ms_xx_constraint_PK_AspNetRoles1] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (FILLFACTOR = 80)
        );
    
        IF EXISTS (SELECT TOP 1 1 
                   FROM   [dbo].[AspNetRoles])
            BEGIN
                INSERT INTO [dbo].[tmp_ms_xx_AspNetRoles] ([Id], [Name], [NormalizedName], [ConcurrencyStamp])
                SELECT   [Id],
                         [Name],
                         UPPER([Name]),
                         NEWID()
                FROM     [dbo].[AspNetRoles]
                ORDER BY [Id] ASC;
            END
    
        DROP TABLE [dbo].[AspNetRoles];
    
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_AspNetRoles]', N'AspNetRoles';
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_constraint_PK_AspNetRoles1]', N'PK_AspNetRoles', N'OBJECT';
    
    COMMIT TRANSACTION;
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    
    GO
    CREATE UNIQUE NONCLUSTERED INDEX [RoleNameIndex]
        ON [dbo].[AspNetRoles]([Name] ASC) WHERE ([Name] IS NOT NULL) WITH (FILLFACTOR = 80);
    
    GO
    CREATE UNIQUE NONCLUSTERED INDEX [RoleNormalizedNameIndex]
        ON [dbo].[AspNetRoles]([NormalizedName] ASC) WHERE ([NormalizedName] IS NOT NULL) WITH (FILLFACTOR = 80);
    
    -------------------------------------------------------------------
    GO
    PRINT N'Starting rebuilding table [dbo].[AspNetUserClaims]...';
    
    GO
    BEGIN TRANSACTION;
        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
        SET XACT_ABORT ON;
    
        CREATE TABLE [dbo].[tmp_ms_xx_AspNetUserClaims] (
            [Id]         INT            IDENTITY (1, 1) NOT NULL,
            [UserId]     NVARCHAR (450) NOT NULL,
            [ClaimType]  NVARCHAR (MAX) NULL,
            [ClaimValue] NVARCHAR (MAX) NULL,
            CONSTRAINT [tmp_ms_xx_constraint_PK_AspNetUserClaims1] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (FILLFACTOR = 80)
        );
    
        IF EXISTS (SELECT TOP 1 1 
                   FROM   [dbo].[AspNetUserClaims])
            BEGIN
                SET IDENTITY_INSERT [dbo].[tmp_ms_xx_AspNetUserClaims] ON;
                INSERT INTO [dbo].[tmp_ms_xx_AspNetUserClaims] ([Id], [UserId], [ClaimType], [ClaimValue])
                SELECT   [Id],
                         [UserId],
                         [ClaimType],
                         [ClaimValue]
                FROM     [dbo].[AspNetUserClaims]
                ORDER BY [Id] ASC;
                SET IDENTITY_INSERT [dbo].[tmp_ms_xx_AspNetUserClaims] OFF;
            END
    
        DROP TABLE [dbo].[AspNetUserClaims];
    
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_AspNetUserClaims]', N'AspNetUserClaims';
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_constraint_PK_AspNetUserClaims1]', N'PK_AspNetUserClaims', N'OBJECT';
    
    COMMIT TRANSACTION;
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    
    GO
    CREATE NONCLUSTERED INDEX [IX_AspNetUserClaims_UserId]
        ON [dbo].[AspNetUserClaims]([UserId] ASC) WITH (FILLFACTOR = 80);
    
    -------------------------------------------------------------------
    GO
    PRINT N'Starting rebuilding table [dbo].[AspNetUserLogins]...';
    
    GO
    BEGIN TRANSACTION;
        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
        SET XACT_ABORT ON;
    
        CREATE TABLE [dbo].[tmp_ms_xx_AspNetUserLogins] (
            [LoginProvider]       NVARCHAR (450) NOT NULL,
            [ProviderKey]         NVARCHAR (450) NOT NULL,
            [ProviderDisplayName] NVARCHAR (MAX) NULL,
            [UserId]              NVARCHAR (450) NOT NULL,
            CONSTRAINT [tmp_ms_xx_constraint_PK_AspNetUserLogins1] PRIMARY KEY CLUSTERED ([LoginProvider] ASC, [ProviderKey] ASC, [UserId] ASC) WITH (FILLFACTOR = 80)
        );
    
        IF EXISTS (SELECT TOP 1 1 
                   FROM   [dbo].[AspNetUserLogins])
            BEGIN
                INSERT INTO [dbo].[tmp_ms_xx_AspNetUserLogins] ([LoginProvider], [ProviderKey], [UserId])
                SELECT   [LoginProvider],
                         [ProviderKey],
                         [UserId]
                FROM     [dbo].[AspNetUserLogins]
                ORDER BY [LoginProvider] ASC, [ProviderKey] ASC;
            END
    
        DROP TABLE [dbo].[AspNetUserLogins];
    
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_AspNetUserLogins]', N'AspNetUserLogins';
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_constraint_PK_AspNetUserLogins1]', N'PK_AspNetUserLogins', N'OBJECT';
    COMMIT TRANSACTION;
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    
    
    GO
    CREATE NONCLUSTERED INDEX [IX_AspNetUserLogins_UserId]
        ON [dbo].[AspNetUserLogins]([UserId] ASC) WITH (FILLFACTOR = 80);
    
    -------------------------------------------------------------------
    GO
    PRINT N'Starting rebuilding table [dbo].[AspNetUserRoles]...';
    
    GO
    BEGIN TRANSACTION;
        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
        SET XACT_ABORT ON;
    
        CREATE TABLE [dbo].[tmp_ms_xx_AspNetUserRoles] (
            [UserId] NVARCHAR (450) NOT NULL,
            [RoleId] NVARCHAR (450) NOT NULL,
            CONSTRAINT [tmp_ms_xx_constraint_PK_AspNetUserRoles1] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC) WITH (FILLFACTOR = 80)
        );
    
        IF EXISTS (SELECT TOP 1 1 
                   FROM   [dbo].[AspNetUserRoles])
            BEGIN
                INSERT INTO [dbo].[tmp_ms_xx_AspNetUserRoles] ([UserId], [RoleId])
                SELECT   [UserId],
                         [RoleId]
                FROM     [dbo].[AspNetUserRoles]
                ORDER BY [UserId] ASC, [RoleId] ASC;
            END
    
        DROP TABLE [dbo].[AspNetUserRoles];
    
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_AspNetUserRoles]', N'AspNetUserRoles';
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_constraint_PK_AspNetUserRoles1]', N'PK_AspNetUserRoles', N'OBJECT';
    
    COMMIT TRANSACTION;
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    
    GO
    CREATE NONCLUSTERED INDEX [IX_AspNetUserRoles_RoleId]
        ON [dbo].[AspNetUserRoles]([RoleId] ASC) WITH (FILLFACTOR = 80);
    -------------------------------------------------------------------
    GO
    PRINT N'Starting rebuilding table [dbo].[AspNetUsers]...';
    
    GO
    BEGIN TRANSACTION;
        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
        SET XACT_ABORT ON;
    
        CREATE TABLE [dbo].[tmp_ms_xx_AspNetUsers] (
            [Id]                   NVARCHAR (450)     NOT NULL,
            [Email]                NVARCHAR (256)     NULL,
            [NormalizedEmail]      NVARCHAR (256)     NULL,
            [EmailConfirmed]       BIT                NOT NULL,
            [PasswordHash]         NVARCHAR (MAX)     NULL,
            [SecurityStamp]        NVARCHAR (MAX)     NULL,
            [PhoneNumber]          NVARCHAR (MAX)     NULL,
            [PhoneNumberConfirmed] BIT                NOT NULL,
            [TwoFactorEnabled]     BIT                NOT NULL,
            [LockoutEndDateUtc]    DATETIME           NULL,
            [LockoutEnd]           DATETIMEOFFSET (7) NULL,
            [LockoutEnabled]       BIT                NOT NULL,
            [AccessFailedCount]    INT                NOT NULL,
            [UserName]             NVARCHAR (256)     NULL,
            [NormalizedUserName]   NVARCHAR (256)     NULL,
            [ConcurrencyStamp]     NVARCHAR (MAX)     NULL,
            CONSTRAINT [tmp_ms_xx_constraint_PK_AspNetUsers1] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (FILLFACTOR = 80)
        );
    
        IF EXISTS (SELECT TOP 1 1 
                   FROM   [dbo].[AspNetUsers])
            BEGIN
                INSERT INTO [dbo].[tmp_ms_xx_AspNetUsers] (
                         [Id], 
                         [Email],
                         [NormalizedEmail],
                         [EmailConfirmed],
                         [PasswordHash],
                         [SecurityStamp],
                         [PhoneNumber],
                         [PhoneNumberConfirmed],
                         [TwoFactorEnabled],
                         [LockoutEnabled],
                         [AccessFailedCount],
                         [UserName],
                         [NormalizedUserName],
                         [ConcurrencyStamp])
                SELECT   [Id],
                         [Email],
                         UPPER([Email]),
                         [EmailConfirmed],
                         [PasswordHash],
                         [SecurityStamp],
                         [PhoneNumber],
                         [PhoneNumberConfirmed],
                         [TwoFactorEnabled],
                         [LockoutEnabled],
                         [AccessFailedCount],
                         [UserName],
                         UPPER([UserName]),
                         NEWID()
                FROM     [dbo].[AspNetUsers]
                ORDER BY [Id] ASC;
            END
    
        DROP TABLE [dbo].[AspNetUsers];
    
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_AspNetUsers]', N'AspNetUsers';
        EXECUTE sp_rename N'[dbo].[tmp_ms_xx_constraint_PK_AspNetUsers1]', N'PK_AspNetUsers', N'OBJECT';
    
    COMMIT TRANSACTION;
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    
    GO
    CREATE UNIQUE NONCLUSTERED INDEX [UserNameIndex]
        ON [dbo].[AspNetUsers]([UserName] ASC) WHERE ([UserName] IS NOT NULL) WITH (FILLFACTOR = 80);
    
    GO
    CREATE UNIQUE NONCLUSTERED INDEX [NormalizedUserNameIndex]
        ON [dbo].[AspNetUsers]([NormalizedUserName] ASC) WHERE ([NormalizedUserName] IS NOT NULL) WITH (FILLFACTOR = 80);
    
    GO
    CREATE NONCLUSTERED INDEX [EmailIndex]
        ON [dbo].[AspNetUsers]([NormalizedEmail] ASC) WITH (FILLFACTOR = 80);
    
    -------------------------------------------------------------------
    GO
    CREATE TABLE [dbo].[AspNetRoleClaims] (
        [Id]         INT            IDENTITY (1, 1) NOT NULL,
        [RoleId]     NVARCHAR (450) NOT NULL,
        [ClaimType]  NVARCHAR (MAX) NULL,
        [ClaimValue] NVARCHAR (MAX) NULL,
        CONSTRAINT [PK_AspNetRoleClaims] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (FILLFACTOR = 80)
    );
    
    GO
    CREATE NONCLUSTERED INDEX [IX_AspNetRoleClaims_RoleId]
        ON [dbo].[AspNetRoleClaims]([RoleId] ASC) WITH (FILLFACTOR = 80);
    
    -------------------------------------------------------------------
    GO
    PRINT N'Creating [dbo].[AspNetUserTokens]...';
    
    GO
    CREATE TABLE [dbo].[AspNetUserTokens] (
        [UserId]        NVARCHAR (450) NOT NULL,
        [LoginProvider] NVARCHAR (450) NOT NULL,
        [Name]          NVARCHAR (450) NOT NULL,
        [Value]         NVARCHAR (MAX) NULL,
        CONSTRAINT [PK_AspNetUserTokens] PRIMARY KEY CLUSTERED ([UserId] ASC, [LoginProvider] ASC, [Name] ASC) WITH (FILLFACTOR = 80)
    );
    
    -------------------------------------------------------------------
    GO
    PRINT N'Creating [dbo].[__EFMigrationsHistory]...';
    
    
    GO
    CREATE TABLE [dbo].[__EFMigrationsHistory] (
        [MigrationId]    NVARCHAR (150) NOT NULL,
        [ProductVersion] NVARCHAR (32)  NOT NULL,
        CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY CLUSTERED ([MigrationId] ASC) WITH (FILLFACTOR = 80)
    );
    
    --GO
    --INSERT INTO [dbo].[__EFMigrationsHistory]
    --           ([MigrationId], [ProductVersion])
    --VALUES
    --            ('20200406184458_InitialCreate',    '2.2.6-servicing-10079')
    -------------------------------------------------------------------
    GO
    PRINT N'Creating constraints';
    
    GO
    ALTER TABLE [dbo].[AspNetUserLogins] WITH NOCHECK
        ADD CONSTRAINT [FK_AspNetUserLogins_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE;
    
    GO
    ALTER TABLE [dbo].[AspNetUserRoles] WITH NOCHECK
        ADD CONSTRAINT [FK_AspNetUserRoles_AspNetRoles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[AspNetRoles] ([Id]) ON DELETE CASCADE;
    
    GO
    ALTER TABLE [dbo].[AspNetUserRoles] WITH NOCHECK
        ADD CONSTRAINT [FK_AspNetUserRoles_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE;
    
    GO
    ALTER TABLE [dbo].[AspNetUserClaims] WITH NOCHECK
        ADD CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE;
    
    GO
    ALTER TABLE [dbo].[AspNetRoleClaims] WITH NOCHECK
        ADD CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[AspNetRoles] ([Id]) ON DELETE CASCADE;
    
    GO
    ALTER TABLE [dbo].[AspNetUserTokens] WITH NOCHECK
        ADD CONSTRAINT [FK_AspNetUserTokens_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE;
    -------------------------------------------------------------------
    
    GO
    PRINT N'Update complete.';
    
    COMMIT TRANSACTION;
    

    【讨论】:

      【解决方案5】:

      虽然 Microsoft documentation 有点帮助。最有用的信息在 cmets 中。

      我能够使用以下步骤迁移现有数据库:

      • 创建一个新的 ASP.net Core 项目并将其 ModelSnapshot 修改为
        与EF6匹配。之后,您可以为 chnages 生成一个脚本 EF6 到 EF Core。

      • 编写脚本来更新 AspNetUsers 表。在 ASP.net 核心中
        身份验证时使用 NormalizedEmail,
        NormalizedUserName 列。所以我们需要更新这两列
        使用我们现有的数据。

      这里是更新表格的脚本的 GitHub 链接。 Migration.zip

      【讨论】:

      • 这似乎是一种有用的方法。您知道(1)如何为更改生成脚本(我意识到我可以使用您指向的脚本,但我想自己尝试)? (2) 这会使项目处于我可以继续在 EF 核心中进行代码优先迁移的状态吗?
      猜你喜欢
      • 1970-01-01
      • 2019-10-26
      • 1970-01-01
      • 2019-08-22
      • 2019-11-15
      • 2011-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多