当 fk 与 pk 不同时如何 link 一对一关系 - EntityFramework
How to link one-to-one relationship when fk is different from pk - EntityFramework
我的数据库中有一个自定义用户 table,我想与 aspnetusers table 建立一对一的关系,这样当我注册一个新客户时,applicationuser class 通过 UserManager 应该将 用户名、电子邮件、密码和学校代码 添加到用户 table 并在其自己的 table 中添加一个 fk。有没有tutorial/example实现这种场景?
我正在尝试添加与我的用户的一对一关系 table 但 fk 不同于 pk
- 用户 Table [列]
- 用户名
- 密码
- 学校代码
- userId [pk]
ASPNETUsers Table [标准列]
- Id, [pk]
- 用户名,
- 密码哈希,
- 安全戳,
- 判别器,
- 电子邮件,
- 电子邮件已确认,
- 电话号码,
- 电话号码已确认,
- 双因素启用,
- LockoutEndDateUtc,
- 已启用锁定,
- 访问失败次数
- userId [fk 给用户 table]
我在ApplicationUser里面添加了属性 class
public class ApplicationUser : IdentityUser
{
public virtual User user { get; set; }
public int UserID { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasRequired(i => i.user).WithRequiredDependent(i => i.appUser);
}
}
public class User
{
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ApplicationUser appUser { get; set; }
}
我还需要做什么,是否正确? entityframework 如何使用这个?
我试图在这里解释我的应用程序结构:
更新
在 运行 上它给我错误
MySql.Data.MySqlClient.MySqlException:'on clause'
中的未知列 'Extent8.appUser_Id'
如果我定义
modelBuilder.Entity<ApplicationUser>().HasKey(u => u.UserID);
在 OnModelCreating 中,然后它尝试 link AspNetRoles 和 Claims 等 table 通过此 UserID,当然会崩溃。
ApplicationUser_Claims_Source_ApplicationUser_Claims_Target: : 引用约束的从属角色中所有属性的类型必须与主体角色中相应的 属性 类型相同。实体 'IdentityUserClaim' 上 属性 'UserId' 的类型与引用约束 [=93] 中实体 'ApplicationUser' 上 属性 'UserID' 的类型不匹配=].
ApplicationUser_Logins_Source_ApplicationUser_Logins_Target: : 引用约束的从属角色中所有属性的类型必须与主体角色中相应的 属性 类型相同。实体 'IdentityUserLogin' 上 属性 'UserId' 的类型与引用约束 [=98] 中实体 'ApplicationUser' 上 属性 'UserID' 的类型不匹配=].
ApplicationUser_Roles_Source_ApplicationUser_Roles_Target: : 引用约束的从属角色中所有属性的类型必须与主体角色中相应的 属性 类型相同。实体 'IdentityUserRole' 上 属性 'UserId' 的类型与引用约束 [=103] 中实体 'ApplicationUser' 上 属性 'UserID' 的类型不匹配=].
简而言之,EF 的一对一关系是如何工作的...
在使用 一对一 关系时,Entity Framework 使用子项中父项的 Id
列作为父项的 FK,它不会为 FK 创建第二列,因为这将被视为 一对多 关系,因为第二列将允许父 Id
存在于许多子条目中,而作为 FK 的 PK 则不会。
我应该如何配置这两个键?
由于您的 ApplicationUser
继承自 IdentityUser
,它将使用它的现有属性和关系。它已经定义了一个 Id
列,因此不需要创建 UserId
,也不需要尝试使 UserId
成为 key 属性 .您应该使用现有的 Id
列,默认情况下它是 string
并且看起来您现有的 User
table 有一个 integer
PK,所以他们没有匹配。
然而,这并不是世界末日。 Identity 的内置实现使用 string
作为其 classes(AspNetUsers、AspNetRoles 等...)的键,但它们允许我们使用另一种类型的主键。
那么...我该怎么办?
Identity为每个table定义了两个class,所以有两个IdentityUser
class,两个IdentityRole
,依此类推。一个是基本泛型 class,它接受一些 泛型 。另一个是该基础 class 的 内置实现 ,它提供那些通用类型。
这个基地class看起来像这样:
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : IdentityUserLogin<TKey>
where TRole : IdentityUserRole<TKey>
where TClaim : IdentityUserClaim<TKey>
第一个泛型类型是TKey
,它定义了键的类型,即string
。第二个是 TLogin
,它是 IdentityUserLogin<TKey>
,这意味着它应该是一些 class 继承 IdentityUserLogin
使用相同的 TKey
类型用作键(也许string
,也许是 int
).
同时,这些基础 classes 的内置实现如下所示:
public class IdentityUser
: IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string> {}
将 TKey
定义为要用作键的 string
。其他泛型,如 IdentityUserLogin
和 IdentityUserRole
,是内置的 classes 实现基础 classes,其中一个 string
键。看看 IdentityUserRole
长什么样:
public class IdentityUserRole IdentityUserRole<string> {}
看到了吗? string
还有。许多其他 classes 使用 string
作为 TKey
的默认值。
因此,如果您要像这样更改 class:
public class ApplicationUser : IdentityUser<int, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public virtual User user { get; set; }
}
还不行,因为我们把TKey
改成了int
,但是内置的IdentityUserLogin
还是用的string
。所以我们需要像这样创建我们自己的IdentityUserLogin
:
public class MyNiceUserLogin : IdentityUserLogin<int>
{
}
...并像这样使用它:
public class ApplicationUser : IdentityUser<int, MyNiceUserLogin, IdentityUserRole, IdentityUserClaim>
{
public virtual User user { get; set; }
}
...但我们仍然必须对其他 class 执行相同的操作,例如 IdentityUserRole
和 IdentityUserClaim
。
我将其配置为使用 int
。那么一对一关系呢?
您的代码:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasRequired(i => i.user).WithRequiredDependent(i => i.appUser);
}
...工作正常。您正在以 必须 存在的方式进行配置,没有 User
就没有 ApplicationUser
,反之亦然。
现在,我建议您阅读 以准确了解要将身份的 class 更改为使用 int
作为密钥应遵循的步骤。
I also strongly recommend you to read this post by John Atten, so you
can understand deeply about how to customize/extend Identity.
我的数据库中有一个自定义用户 table,我想与 aspnetusers table 建立一对一的关系,这样当我注册一个新客户时,applicationuser class 通过 UserManager 应该将 用户名、电子邮件、密码和学校代码 添加到用户 table 并在其自己的 table 中添加一个 fk。有没有tutorial/example实现这种场景?
我正在尝试添加与我的用户的一对一关系 table 但 fk 不同于 pk
- 用户 Table [列]
- 用户名
- 密码
- 学校代码
- userId [pk]
ASPNETUsers Table [标准列]
- Id, [pk]
- 用户名,
- 密码哈希,
- 安全戳,
- 判别器,
- 电子邮件,
- 电子邮件已确认,
- 电话号码,
- 电话号码已确认,
- 双因素启用,
- LockoutEndDateUtc,
- 已启用锁定,
- 访问失败次数
- userId [fk 给用户 table]
我在ApplicationUser里面添加了属性 class
public class ApplicationUser : IdentityUser
{
public virtual User user { get; set; }
public int UserID { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasRequired(i => i.user).WithRequiredDependent(i => i.appUser);
}
}
public class User
{
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ApplicationUser appUser { get; set; }
}
我还需要做什么,是否正确? entityframework 如何使用这个?
我试图在这里解释我的应用程序结构:
更新 在 运行 上它给我错误 MySql.Data.MySqlClient.MySqlException:'on clause'
中的未知列 'Extent8.appUser_Id'如果我定义
modelBuilder.Entity<ApplicationUser>().HasKey(u => u.UserID);
在 OnModelCreating 中,然后它尝试 link AspNetRoles 和 Claims 等 table 通过此 UserID,当然会崩溃。
ApplicationUser_Claims_Source_ApplicationUser_Claims_Target: : 引用约束的从属角色中所有属性的类型必须与主体角色中相应的 属性 类型相同。实体 'IdentityUserClaim' 上 属性 'UserId' 的类型与引用约束 [=93] 中实体 'ApplicationUser' 上 属性 'UserID' 的类型不匹配=]. ApplicationUser_Logins_Source_ApplicationUser_Logins_Target: : 引用约束的从属角色中所有属性的类型必须与主体角色中相应的 属性 类型相同。实体 'IdentityUserLogin' 上 属性 'UserId' 的类型与引用约束 [=98] 中实体 'ApplicationUser' 上 属性 'UserID' 的类型不匹配=]. ApplicationUser_Roles_Source_ApplicationUser_Roles_Target: : 引用约束的从属角色中所有属性的类型必须与主体角色中相应的 属性 类型相同。实体 'IdentityUserRole' 上 属性 'UserId' 的类型与引用约束 [=103] 中实体 'ApplicationUser' 上 属性 'UserID' 的类型不匹配=].
简而言之,EF 的一对一关系是如何工作的...
在使用 一对一 关系时,Entity Framework 使用子项中父项的 Id
列作为父项的 FK,它不会为 FK 创建第二列,因为这将被视为 一对多 关系,因为第二列将允许父 Id
存在于许多子条目中,而作为 FK 的 PK 则不会。
我应该如何配置这两个键?
由于您的 ApplicationUser
继承自 IdentityUser
,它将使用它的现有属性和关系。它已经定义了一个 Id
列,因此不需要创建 UserId
,也不需要尝试使 UserId
成为 key 属性 .您应该使用现有的 Id
列,默认情况下它是 string
并且看起来您现有的 User
table 有一个 integer
PK,所以他们没有匹配。
然而,这并不是世界末日。 Identity 的内置实现使用 string
作为其 classes(AspNetUsers、AspNetRoles 等...)的键,但它们允许我们使用另一种类型的主键。
那么...我该怎么办?
Identity为每个table定义了两个class,所以有两个IdentityUser
class,两个IdentityRole
,依此类推。一个是基本泛型 class,它接受一些 泛型 。另一个是该基础 class 的 内置实现 ,它提供那些通用类型。
这个基地class看起来像这样:
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : IdentityUserLogin<TKey>
where TRole : IdentityUserRole<TKey>
where TClaim : IdentityUserClaim<TKey>
第一个泛型类型是TKey
,它定义了键的类型,即string
。第二个是 TLogin
,它是 IdentityUserLogin<TKey>
,这意味着它应该是一些 class 继承 IdentityUserLogin
使用相同的 TKey
类型用作键(也许string
,也许是 int
).
同时,这些基础 classes 的内置实现如下所示:
public class IdentityUser
: IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string> {}
将 TKey
定义为要用作键的 string
。其他泛型,如 IdentityUserLogin
和 IdentityUserRole
,是内置的 classes 实现基础 classes,其中一个 string
键。看看 IdentityUserRole
长什么样:
public class IdentityUserRole IdentityUserRole<string> {}
看到了吗? string
还有。许多其他 classes 使用 string
作为 TKey
的默认值。
因此,如果您要像这样更改 class:
public class ApplicationUser : IdentityUser<int, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public virtual User user { get; set; }
}
还不行,因为我们把TKey
改成了int
,但是内置的IdentityUserLogin
还是用的string
。所以我们需要像这样创建我们自己的IdentityUserLogin
:
public class MyNiceUserLogin : IdentityUserLogin<int>
{
}
...并像这样使用它:
public class ApplicationUser : IdentityUser<int, MyNiceUserLogin, IdentityUserRole, IdentityUserClaim>
{
public virtual User user { get; set; }
}
...但我们仍然必须对其他 class 执行相同的操作,例如 IdentityUserRole
和 IdentityUserClaim
。
我将其配置为使用 int
。那么一对一关系呢?
您的代码:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasRequired(i => i.user).WithRequiredDependent(i => i.appUser);
}
...工作正常。您正在以 必须 存在的方式进行配置,没有 User
就没有 ApplicationUser
,反之亦然。
现在,我建议您阅读 int
作为密钥应遵循的步骤。
I also strongly recommend you to read this post by John Atten, so you can understand deeply about how to customize/extend Identity.