使用 SQL 脚本创建 ASP.NET 标识表

Posted

技术标签:

【中文标题】使用 SQL 脚本创建 ASP.NET 标识表【英文标题】:Create ASP.NET Identity tables using SQL script 【发布时间】:2013-11-24 19:51:12 【问题描述】:

我正在尝试将 ASP.NET Identity 合并到一个新应用程序中,该应用程序当前使用 SQL 脚本来创建数据库架构。由于我们需要创建从其他表到用户表的外键约束,因此非常希望在相同的脚本中也创建 ASP.NET 标识表。

我已经能够在 IdentityModels.cs 中创建的 ApplicationUser 类中扩展 IdentityUser 类-

public class ApplicationUser : IdentityUser

    public ApplicationUser()
    
        Sequence = 0;
        LastActivity = DateTime.Now;
    

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int NumericId
    
        get;
        set;
    

    [MaxLength(50), Required]
    public string DisplayName
    
        get;
        set;
    

    [MaxLength(50), Required]
    public string Description
    
        get;
        set;
    

    [IntegerValidator(MinValue = 0), Required]
    public int Sequence
    
        get;
        set;
    

    [MaxLength(50)]
    public string ExternalRef
    
        get;
        set;
    

    public DateTime? LoggedOn
    
        get;
        set;
    

    public DateTime? LoggedOff
    
        get;
        set;
    

    public DateTime LastActivity
    
        get;
        set;
    

    public int FailedLoginAttempts
    
        get;
        set;
    

    public DateTime? LockedOutUntil
    
        get;
        set;
    

    public int LockOutCycles
    
        get;
        set;
    

    public bool Approved
    
        get;
        set;
    

我已经使用脚本创建了表格-

CREATE TABLE [Users].[User] (
[Id] [nvarchar](128) NOT NULL
,[NumericId] [int] IDENTITY(1,1) NOT NULL
,[UserName] [nvarchar](50) NULL
,[PasswordHash] [nvarchar](max) NULL
,[SecurityStamp] [nvarchar](max) NULL
,[DisplayName] [nvarchar](50) NULL
,[Description] [nvarchar](50) NOT NULL
,[EmailAddress] [nvarchar](254) NOT NULL
,[Confirmed] [bit] NOT NULL
,[Sequence] [int] NOT NULL
,[ExternalRef] [nvarchar](50) NOT NULL
,[LoggedOn] [datetime] NULL
,[LoggedOff] [datetime] NULL
,[LastActivity] [datetime] NULL
,[FailedLoginAttempts] [int] NOT NULL
,[LockedOutUntil] [datetime] NULL
,[LockOutCycles] int NOT NULL
,[Approved] [bit] NOT NULL
,[Discriminator] [nvarchar](128) NOT NULL
,CONSTRAINT [PK_User] PRIMARY KEY NONCLUSTERED ([Id] ASC) WITH (
    PAD_INDEX = OFF
    ,STATISTICS_NORECOMPUTE = OFF
    ,IGNORE_DUP_KEY = OFF
    ,ALLOW_ROW_LOCKS = ON
    ,ALLOW_PAGE_LOCKS = ON
    ) ON [PRIMARY]
,CONSTRAINT [IX_User_NumericId] UNIQUE CLUSTERED ([NumericId] ASC) WITH (
    PAD_INDEX = OFF
    ,STATISTICS_NORECOMPUTE = OFF
    ,IGNORE_DUP_KEY = OFF
    ,ALLOW_ROW_LOCKS = ON
    ,ALLOW_PAGE_LOCKS = ON
    ) ON [PRIMARY]
,CONSTRAINT [IX_User_Name] UNIQUE NONCLUSTERED ([UserName] 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 [Users].[User] ADD CONSTRAINT [DF_User_Description] DEFAULT('')
FOR [Description]
GO

ALTER TABLE [Users].[User] ADD CONSTRAINT [DF_User_Sequence] DEFAULT((0))
FOR [Sequence]
GO

ALTER TABLE [Users].[User] ADD CONSTRAINT [DF_User_ExternalRef] DEFAULT('')
FOR [ExternalRef]
GO

ALTER TABLE [Users].[User] ADD CONSTRAINT [DF_User_FailedLoginAttempts] DEFAULT((0))
FOR [FailedLoginAttempts]
GO

ALTER TABLE [Users].[User] ADD CONSTRAINT [DF_User_LockOutCycles] DEFAULT((0))
FOR [LockOutCycles]
GO

CREATE NONCLUSTERED INDEX [IX_User_Sequence] ON [Users].[User] ([Sequence] ASC, [UserName] ASC)
    WITH (
            PAD_INDEX = OFF
            ,STATISTICS_NORECOMPUTE = OFF
            ,SORT_IN_TEMPDB = OFF
            ,IGNORE_DUP_KEY = OFF
            ,DROP_EXISTING = OFF
            ,ONLINE = OFF
            ,ALLOW_ROW_LOCKS = ON
            ,ALLOW_PAGE_LOCKS = ON
            ) ON [PRIMARY]
GO

CREATE TABLE [Users].[UserClaim](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ClaimType] [nvarchar](max) NULL,
    [ClaimValue] [nvarchar](max) NULL,
    [UserId] [nvarchar](128) NOT NULL,
 CONSTRAINT [PK_Users.UserClaims] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, 
        STATISTICS_NORECOMPUTE = OFF, 
        IGNORE_DUP_KEY = OFF, 
        ALLOW_ROW_LOCKS = ON, 
        ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [Users].[UserClaim]  WITH CHECK ADD  CONSTRAINT [FK_Users.UserClaims_Users.User_User_Id] FOREIGN KEY([UserId])
REFERENCES [Users].[User] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [Users].[UserClaim] CHECK CONSTRAINT [FK_Users.UserClaims_Users.User_User_Id]
GO

CREATE TABLE [Users].[UserLogin](
    [UserId] [nvarchar](128) NOT NULL,
    [LoginProvider] [nvarchar](128) NOT NULL,
    [ProviderKey] [nvarchar](128) NOT NULL,
 CONSTRAINT [PK_Users.UserLogins] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC,
    [LoginProvider] ASC,
    [ProviderKey] 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 [Users].[UserLogin]  WITH CHECK ADD  CONSTRAINT [FK_Users.UserLogins_Users.User_UserId] FOREIGN KEY([UserId])
REFERENCES [Users].[User] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [Users].[UserLogin] CHECK CONSTRAINT [FK_Users.UserLogins_Users.User_UserId]

CREATE TABLE [Users].[ApplicationRole](
    [Id] [nvarchar](128) NOT NULL,
    [Name] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_Users.ApplicationRole] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, 
        STATISTICS_NORECOMPUTE = OFF, 
        IGNORE_DUP_KEY = OFF, 
        ALLOW_ROW_LOCKS = ON, 
        ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]


CREATE TABLE [Users].[UserRole](
    [UserId] [nvarchar](128) NOT NULL,
    [RoleId] [nvarchar](128) NOT NULL,
 CONSTRAINT [PK_Users.UserRole] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC,
    [RoleId] 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 [Users].[UserRole]  WITH CHECK ADD  CONSTRAINT [FK_Users.UserRole_Users.ApplicationRole_RoleId] FOREIGN KEY([RoleId])
REFERENCES [Users].[ApplicationRole] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [Users].[UserRole] CHECK CONSTRAINT [FK_Users.UserRole_Users.ApplicationRole_RoleId]
GO

ALTER TABLE [Users].[UserRole]  WITH CHECK ADD  CONSTRAINT [FK_Users.UserRole_Users.User_UserId] FOREIGN KEY([UserId])
REFERENCES [Users].[User] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [Users].[UserRole] CHECK CONSTRAINT [FK_Users.UserRole_Users.User_UserId]
GO

CREATE TABLE [Users].[Department](
    [Id] [int] IDENTITY(1, 1) NOT NULL
    ,[Name] [nvarchar](50) NOT NULL
    ,[Description] [nvarchar](50) NOT NULL
    ,[Sequence] [int] NOT NULL
 CONSTRAINT [PK_Users.Department] PRIMARY KEY CLUSTERED 
(
    [Id] 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

并已使用 OnModelCreating 事件的覆盖将实体映射到表-

public class ApplicationDbContext : IdentityDbContext<IdentityUser>

    public ApplicationDbContext()
        : base("DefaultConnection")
    
    

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        modelBuilder.Entity<IdentityUser>().ToTable("User", "Users");

        modelBuilder.Entity<IdentityUser>().Property(iu => iu.Id).HasColumnName("Id");
        modelBuilder.Entity<IdentityUser>().Property(iu => iu.UserName).HasColumnName("UserName");
        modelBuilder.Entity<IdentityUser>().Property(iu => iu.Email).HasColumnName("EmailAddress").HasMaxLength(254).IsRequired();
        modelBuilder.Entity<IdentityUser>().Property(iu => iu.PasswordHash).HasColumnName("PasswordHash");
        modelBuilder.Entity<IdentityUser>().Property(iu => iu.SecurityStamp).HasColumnName("SecurityStamp");
        modelBuilder.Entity<IdentityUser>().Property(iu => iu.IsConfirmed).HasColumnName("Confirmed");

        modelBuilder.Entity<ApplicationUser>().HasKey(au => au.Id).ToTable("User", "Users"); //Specify our our own table names instead of the defaults

        modelBuilder.Entity<ApplicationUser>().Property(au => au.Id).HasColumnName("Id");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.NumericId).HasColumnName("NumericId");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.UserName).HasMaxLength(50).HasColumnName("UserName");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.PasswordHash).HasColumnName("PasswordHash");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.SecurityStamp).HasColumnName("SecurityStamp");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.DisplayName).HasColumnName("DisplayName");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.Description).HasColumnName("Description");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.Sequence).HasColumnName("Sequence");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.ExternalRef).HasColumnName("ExternalRef");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.LoggedOn).HasColumnName("LoggedOn");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.LoggedOff).HasColumnName("LoggedOff");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.LastActivity).HasColumnName("LastActivity");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.FailedLoginAttempts).IsOptional().HasColumnName("FailedLoginAttempts");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.LockedOutUntil).IsOptional().HasColumnName("LockedOutUntil");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.LockOutCycles).IsOptional().HasColumnName("LockOutCycles");
        modelBuilder.Entity<ApplicationUser>().Property(au => au.Approved).HasColumnName("Approved");

        modelBuilder.Entity<IdentityRole>().HasKey(ir => ir.Id).ToTable("ApplicationRole", "Users");

        modelBuilder.Entity<IdentityRole>().Property(ir => ir.Id).HasColumnName("Id");
        modelBuilder.Entity<IdentityRole>().Property(ir => ir.Name).HasColumnName("Name");

        modelBuilder.Entity<IdentityUserClaim>().HasKey(iuc => iuc.Id).ToTable("UserClaim", "Users");

        modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.Id).HasColumnName("Id");
        modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.ClaimType).HasColumnName("ClaimType");
        modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.ClaimValue).HasColumnName("ClaimValue");
        modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.UserId).HasColumnName("UserId");

        modelBuilder.Entity<IdentityUserLogin>().HasKey(iul => new  iul.UserId, iul.LoginProvider, iul.ProviderKey ).ToTable("UserLogin", "Users"); //Used for third party OAuth providers

        modelBuilder.Entity<IdentityUserLogin>().Property(iul => iul.UserId).HasColumnName("UserId");
        modelBuilder.Entity<IdentityUserLogin>().Property(iul => iul.LoginProvider).HasColumnName("LoginProvider");
        modelBuilder.Entity<IdentityUserLogin>().Property(iul => iul.ProviderKey).HasColumnName("ProviderKey");

        modelBuilder.Entity<IdentityUserRole>().HasKey(iur => new  iur.UserId, iur.RoleId ).ToTable("UserRole", "Users");

        modelBuilder.Entity<IdentityUserRole>().Property(ur => ur.UserId).HasColumnName("UserId");
        modelBuilder.Entity<IdentityUserRole>().Property(ur => ur.RoleId).HasColumnName("RoleId");
    

这对于注册来说效果很好,并且确实在注册后登录,但是在此之后任何尝试登录都会导致-

异常详情:System.Data.SqlClient.SqlException:

列名“IdentityUser_Id”无效。

列名“IdentityUser_Id”无效。

列名“Id”无效。 列名“IdentityRole_Id”无效。

列名“IdentityUser_Id”无效。

来源错误:

第 337 行:

第 338 行: AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

第 339 行: var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

第 340 行: AuthenticationManager.SignIn(new AuthenticationProperties IsPersistent = isPersistent , 身份);

第 341 行:

我认为这是 ApplicationUser 和 IdentityUserRole 实体之间的外键问题 - 这些存在于数据库中,但未在流式 API 映射中定义。 IdentityUser 是一个复杂类型的事实似乎导致 EF 假设这些列基于生成的查询附加到 IdentityUserRole 表(名为“[User].[UserRole]”) -

exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[UserId] AS [UserId], 
[Extent1].[RoleId] AS [RoleId], 
[Extent1].[IdentityRole_Id] AS [IdentityRole_Id], 
[Extent1].[IdentityUser_Id] AS [IdentityUser_Id]
FROM [Users].[UserRole] AS [Extent1]
WHERE ([Extent1].[IdentityUser_Id] IS NOT NULL) AND ([Extent1].[IdentityUser_Id] =    
@EntityKeyValue1)',N'@EntityKeyValue1 nvarchar(128)',
@EntityKeyValue1=N'2e16b5b5-6604-4f2c-9fbb-7cc3f5c9d4f3'
go

如何在 fluent API 中配置外键,或者从 ApplicationUser/IdentityUser 和 IdentityRole 类 o 指向“[Users].[User]”表,或者从 IdentityUserRole 类指向用户和角色实体或表? SQL 中已经存在外键。

【问题讨论】:

【参考方案1】:

因此,默认情况下,新的 1.1-alpha1 位将具有以下内容。这可能是您正在寻找的关于外键的内容。注意:这与 1.0 有点不同,因为导航属性发生了一些变化,以启用指定主键类型的能力:

我们正在尝试解决一些 EF 迁移/可扩展性问题,因此希望使用 Identity 1.1-alpha1 和即将发布的 6.0.2/6.1 EF 版本更容易,但我不确定更新的 EF 包是否还可以在 myget 上使用。

    var user = modelBuilder.Entity<TUser>()
        .ToTable("AspNetUsers");
    user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
    user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
    user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
    user.Property(u => u.UserName).IsRequired();

    modelBuilder.Entity<TUserRole>()
        .HasKey(r => new  r.UserId, r.RoleId )
        .ToTable("AspNetUserRoles");

    modelBuilder.Entity<TUserLogin>()
        .HasKey(l => new  l.UserId, l.LoginProvider, l.ProviderKey)
        .ToTable("AspNetUserLogins");

    modelBuilder.Entity<TUserClaim>()
        .ToTable("AspNetUserClaims");

    var role = modelBuilder.Entity<TRole>()
        .ToTable("AspNetRoles");
    role.Property(r => r.Name).IsRequired();
    role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);

【讨论】:

我想补充一点,你们强迫我们接受身份,这很糟糕。 Asp.net Membership 非常漂亮,因为它刚刚好用。我们有一个配置网站,可以在 VS 中启动以设置用户/角色等。有时我收到构建简单应用程序的请求,但我没有时间花几个小时试图弄清楚如何使用 Identity,这给我没有比以前的会员系统更好的功能。【参考方案2】:

最终(工作)OnModelCreating 方法建立在 Hao Kung 提供的答案(我已接受他的答案并且我已向其授予赏金)的基础上,从 IdentityUser/ApplicationUser 端创建外键引用,而不是尝试做另一个方向也一样。

protected override void OnModelCreating(DbModelBuilder modelBuilder)

    base.OnModelCreating(modelBuilder);

    var user = modelBuilder.Entity<IdentityUser>().HasKey(u => u.Id).ToTable("User", "Users"); //Specify our our own table names instead of the defaults

    user.Property(iu => iu.Id).HasColumnName("Id");
    user.Property(iu => iu.UserName).HasColumnName("UserName");
    user.Property(iu => iu.Email).HasColumnName("EmailAddress").HasMaxLength(254).IsRequired();
    user.Property(iu => iu.IsConfirmed).HasColumnName("EmailConfirmed");
    user.Property(iu => iu.PasswordHash).HasColumnName("PasswordHash");
    user.Property(iu => iu.SecurityStamp).HasColumnName("SecurityStamp");

    user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
    user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
    user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
    user.Property(u => u.UserName).IsRequired();

    var applicationUser = modelBuilder.Entity<ApplicationUser>().HasKey(au => au.Id).ToTable("User", "Users"); //Specify our our own table names instead of the defaults

    applicationUser.Property(au => au.Id).HasColumnName("Id");
    applicationUser.Property(au => au.NumericId).HasColumnName("NumericId");
    applicationUser.Property(au => au.UserName).HasMaxLength(50).HasColumnName("UserName");
    applicationUser.Property(au => au.PasswordHash).HasColumnName("PasswordHash");
    applicationUser.Property(au => au.SecurityStamp).HasColumnName("SecurityStamp");
    applicationUser.Property(au => au.DisplayName).HasColumnName("DisplayName");
    applicationUser.Property(au => au.Description).HasColumnName("Description");
    applicationUser.Property(au => au.Email).HasColumnName("EmailAddress").HasMaxLength(254).IsRequired();
    applicationUser.Property(au => au.IsConfirmed).HasColumnName("EmailConfirmed");
    applicationUser.Property(au => au.Sequence).HasColumnName("Sequence");
    applicationUser.Property(au => au.ExternalRef).HasColumnName("ExternalRef");
    applicationUser.Property(au => au.LoggedOn).HasColumnName("LoggedOn");
    applicationUser.Property(au => au.LoggedOff).HasColumnName("LoggedOff");
    applicationUser.Property(au => au.LastActivity).HasColumnName("LastActivity");
    applicationUser.Property(au => au.FailedLoginAttempts).IsOptional().HasColumnName("FailedLoginAttempts");
    applicationUser.Property(au => au.LockedOutUntil).IsOptional().HasColumnName("LockedOutUntil");
    applicationUser.Property(au => au.LockOutCycles).IsOptional().HasColumnName("LockOutCycles");
    applicationUser.Property(au => au.Approved).HasColumnName("Approved");

    var role = modelBuilder.Entity<IdentityRole>().HasKey(ir => ir.Id).ToTable("ApplicationRole", "Users");

    role.Property(ir => ir.Id).HasColumnName("Id");
    role.Property(ir => ir.Name).HasColumnName("Name");

    var claim = modelBuilder.Entity<IdentityUserClaim>().HasKey(iuc => iuc.Id).ToTable("UserClaim", "Users");

    claim.Property(iuc => iuc.Id).HasColumnName("Id");
    claim.Property(iuc => iuc.ClaimType).HasColumnName("ClaimType");
    claim.Property(iuc => iuc.ClaimValue).HasColumnName("ClaimValue");
    claim.Property(iuc => iuc.UserId).HasColumnName("UserId");

    var login = modelBuilder.Entity<IdentityUserLogin>().HasKey(iul => new  iul.UserId, iul.LoginProvider, iul.ProviderKey ).ToTable("UserLogin", "Users"); //Used for third party OAuth providers

    login.Property(iul => iul.UserId).HasColumnName("UserId");
    login.Property(iul => iul.LoginProvider).HasColumnName("LoginProvider");
    login.Property(iul => iul.ProviderKey).HasColumnName("ProviderKey");

    var userRole = modelBuilder.Entity<IdentityUserRole>().HasKey(iur => new  iur.UserId, iur.RoleId ).ToTable("UserRole", "Users");

    userRole.Property(ur => ur.UserId).HasColumnName("UserId");
    userRole.Property(ur => ur.RoleId).HasColumnName("RoleId");

我也喜欢将模型生成器实体引用为变量以在每个后续的 fluent-api 语句中使用的想法(出色但如此简单,我想不出我以前为什么不这样做)。

我在两个实体上都引用了属性和外键,尽管(实际上)这似乎不是必需的。在 ASP.NET Identity 1.0 上,省略属性映射没有问题,但由于我已经更改了 Email 和 IsConfirmed 数据库列的名称,至少那些需要映射到 ASP.NET Identity 1.1 中的 IdentityUser 实体

同样,ApplicationUser 类可能不需要定义外键(因为在基类和其他实体之间定义了关系),但我认为确保在其他实体表上没有额外外键列的假设没有什么坏处通过添加一个明确的定义 - 它似乎没有造成任何伤害。

【讨论】:

您的 OnModelCreating 几乎帮助我回答了我的问题***.com/questions/20251129/…。如果您在该线程上发帖,我会为您提供答案。【参考方案3】:

不确定您要达到的具体目标。如果您根据需要使用以下内容应该可以:

modelBuilder.ComplexType // 告诉 ef 这是一个复杂的属性,只是嵌入到表中, modelBuilder.Ignore // 不需要表。

那么在映射外键字段时,如果你有一个持有外键的属性,告诉ef

modelBuilder.Entity<T>().HasRequired(t => t.NAVPROP)
              .WithMany()
              .HasForeignKey(t => t.PropertyinTThatholdsFKID);   

field_id 由 EF 在发现需要满足已定义的关系并且没有已定义的外键字段时生成。所以要么你有你不期望或不想要的额外关系。或者没有用外键字段指定关系。

【讨论】:

【参考方案4】:

请参阅How can I change the table names when using Visual Studio 2013 ASP.NET Identity? 了解更多信息(初始问题的底部)。我认为您只需要在流畅调用之前调用 base.onModelCreating(modelBuilder) 即可。

在您流利的通话之前,添加:

base.onModelCreating(modelBuilder);

就 SQL 脚本而言:

Update-Database -Script

另外,here 是一个代码项目,其架构已经编写好了脚本(尽管我意识到表名不同)。

编辑 我重写的 OnModelCreating 方法(注意在开头调用 base.OnModelCreating(modelBuilder) ):

protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
        
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<IdentityUser>().ToTable("User");

            modelBuilder.Entity<IdentityUser>().Property(iu => iu.Id).HasColumnName("Id");
            modelBuilder.Entity<IdentityUser>().Property(iu => iu.UserName).HasColumnName("UserName");

            /*IdentityUser does not have an Email property
            modelBuilder.Entity<IdentityUser>().Property(iu => iu.Email).HasColumnName("EmailAddress").HasMaxLength(254).IsRequired(); Not sure where this is from?*/

            modelBuilder.Entity<IdentityUser>().Property(iu => iu.PasswordHash).HasColumnName("PasswordHash");
            modelBuilder.Entity<IdentityUser>().Property(iu => iu.SecurityStamp).HasColumnName("SecurityStamp");

            /*Same - dont know where this is from?
            modelBuilder.Entity<IdentityUser>().Property(iu => iu.IsConfirmed).HasColumnName("Confirmed");*/


            modelBuilder.Entity<ApplicationUser>().HasKey(au => au.Id).ToTable("User"); //Specify our our own table names instead of the defaults

            modelBuilder.Entity<ApplicationUser>().Property(au => au.Id).HasColumnName("Id");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.NumericId).HasColumnName("NumericId");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.UserName).HasMaxLength(50).HasColumnName("UserName");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.SecurityStamp).HasColumnName("SecurityStamp");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.DisplayName).HasColumnName("DisplayName");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.Description).HasColumnName("Description");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.Sequence).HasColumnName("Sequence");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.ExternalRef).HasColumnName("ExternalRef");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.LoggedOn).HasColumnName("LoggedOn");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.LoggedOff).HasColumnName("LoggedOff");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.LastActivity).HasColumnName("LastActivity");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.FailedLoginAttempts).IsOptional().HasColumnName("FailedLoginAttempts");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.LockedOutUntil).IsOptional().HasColumnName("LockedOutUntil");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.LockOutCycles).IsOptional().HasColumnName("LockOutCycles");
            modelBuilder.Entity<ApplicationUser>().Property(au => au.Approved).HasColumnName("Approved");

            modelBuilder.Entity<IdentityRole>().HasKey(ir => ir.Id).ToTable("ApplicationRole");

            modelBuilder.Entity<IdentityRole>().Property(ir => ir.Id).HasColumnName("Id");
            modelBuilder.Entity<IdentityRole>().Property(ir => ir.Name).HasColumnName("Name");

            modelBuilder.Entity<IdentityUserClaim>().HasKey(iuc => iuc.Id).ToTable("UserClaim");

            modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.Id).HasColumnName("Id");
            modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.ClaimType).HasColumnName("ClaimType");
            modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.ClaimValue).HasColumnName("ClaimValue");
            //modelBuilder.Entity<IdentityUserClaim>().Property(iuc => iuc.UserId).HasColumnName("UserId");

            modelBuilder.Entity<IdentityUserLogin>().HasKey(iul => new  iul.UserId, iul.LoginProvider, iul.ProviderKey ).ToTable("UserLogin"); //Used for third party OAuth providers

            modelBuilder.Entity<IdentityUserLogin>().Property(iul => iul.UserId).HasColumnName("UserId");
            modelBuilder.Entity<IdentityUserLogin>().Property(iul => iul.LoginProvider).HasColumnName("LoginProvider");
            modelBuilder.Entity<IdentityUserLogin>().Property(iul => iul.ProviderKey).HasColumnName("ProviderKey");

            modelBuilder.Entity<IdentityUserRole>().HasKey(iur => new  iur.UserId, iur.RoleId ).ToTable("UserRole");

            modelBuilder.Entity<IdentityUserRole>().Property(ur => ur.UserId).HasColumnName("UserId");
            modelBuilder.Entity<IdentityUserRole>().Property(ur => ur.RoleId).HasColumnName("RoleId");


        

【讨论】:

我已按照您的建议完成了 - 启用自动迁移并在包管理器控制台中运行 Update-Datebase -Script 生成了 SQL -“CREATE TABLE [Users].[UserRole] ([UserId] [nvarchar] ](128) 非空,[RoleId] [nvarchar](128) 非空,[Id] [int] 非空,[IdentityRole_Id] [nvarchar](128),[IdentityUser_Id] [nvarchar](128),约束 [ PK_Users.UserRole] PRIMARY KEY ([UserId], [RoleId]) )" - 表明外键仍然是一个问题。 将配置复制到一个新项目(没有数据库)我得到以下生成的 SQL-“CREATE TABLE [Users].[UserRole] ([UserId] [nvarchar](128) NOT NULL, [RoleId] [nvarchar](128) NOT NULL, CONSTRAINT [PK_Users.UserRole] PRIMARY KEY ([UserId], [RoleId]) )" 我从未说过启用自动迁移。你添加了 base.onModelCreating(modelBuilder);到您重写的 OnModelCreating 的顶部?我已经用重写的 OnModelCreating 更新了答案。 我确实添加了那个。 Update-Database -Script 要求启用迁移并将自动迁移设置为 true。电子邮件和确认的属性存在于每晚构建中,我一直在尝试稳定和每晚都得到相同的结果。 啊好吧 - 我知道它确实需要启用迁移,但我从未见过它需要启用自动迁移。我测试了这段代码,看到了 FK 的工作——我不确定你的为什么不创建关系。

以上是关于使用 SQL 脚本创建 ASP.NET 标识表的主要内容,如果未能解决你的问题,请参考以下文章

sQL数据库表的主键列设为标识,增量为1,下次插入数据时能不插入主键列吗

ASP.NET 标识 2 到 3

sql 用于mysql的asp.net标识

从数据库中删除 ASP.NET 标识表

无法使用 ASP .NET Core 更新 SQL 位列

ASP.NET 标识和声明