当fk与pk - EntityFramework不同时,如何链接一对一的关系 [英] How to link one-to-one relationship when fk is different from pk - EntityFramework

查看:308
本文介绍了当fk与pk - EntityFramework不同时,如何链接一对一的关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的数据库中有一个自定义用户表,我想与aspnetusers表创建一个一对一的关系,所以当我注册一个新客户时,通过UserManager的applicationuser类应该添加用户名,电子邮件,密码和学校代码,并在自己的表中添加一个fk。有没有任何教程/示例实现这种情况?



我正在尝试与我的用户表添加一对一的关系,但是fk与pk不同




  • 用户表[列]


    • 用户名

    • 密码

    • 学校代码

    • userId [pk]

    li>


ASPNETUsers表[标准列]




  • Id ,[pk]

  • UserName,

  • PasswordHash,

  • SecurityStamp

  • 鉴别器

  • 电子邮件

  • EmailConfirmed,

  • PhoneNumber,

  • PhoneNumberConfirmed,

  • TwoFactorEnabled,

  • LockoutEndDateUtc,

  • LockoutEnabled,

  • AccessFailedCount

  • userId [fk to users table]



我已经在 ApplicationUser 类中添加了属性

  public cla ss ApplicationUser:IdentityUser 
{
public virtual User user {get;组; }
public int UserID {get;组; }
}

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;组; }
public string Username {get;组; }
public string Password {get;组; }
public virtual ApplicationUser appUser {get;组; }
}

我还需要做什么,是否正确?我们已经尝试在这里解释我的应用程序结构:



更新
运行时会给我错误
MySql.Data.MySqlClient.MySqlException:未知列'Extent8.appUser_Id'in'on条款'



如果我定义

  modelBuilder.Entity< ApplicationUser> ().HasKey(u => u.UserID); 

内部 OnModelCreating ,然后尝试通过此链接将AspNetRoles和Claims etc表链接UserID和thecourse崩溃。



ApplicationUser_Claims_Source_ApplicationUser_Claims_Target::引用约束的从属角色中的所有属性的类型必须与主体角色中相应的属性类型相同。实体IdentityUserClaim上的属性UserId的类型与引用约束ApplicationUser_Claims中的实体ApplicationUser上的属性UserID的类型不匹配。
ApplicationUser_Logins_Source_ApplicationUser_Logins_Target::引用约束的从属角色中的所有属性的类型必须与主体角色中相应的属性类型相同。实体IdentityUserLogin上的属性UserId的类型与引用约束ApplicationUser_Logins中的实体ApplicationUser上的属性UserID的类型不匹配。
ApplicationUser_Roles_Source_ApplicationUser_Roles_Target:参考约束的从属角色中的所有属性的类型必须与主体角色中相应的属性类型相同。实体IdentityUserRole上的属性UserId的类型与引用约束ApplicationUser_Roles中实体ApplicationUser上的属性UserID的类型不匹配。

解决方案

EF的一对一关系如何工作,简而言之...



当使用一个关系,实体框架使用父项的 Id 列作为父项的FK,它不会为FK创建第二列,因为这将被认为是一对多关系,因为第二列将允许父项 Id 存在于许多子条目中,而PK作为FK



如何配置两个键?



由于您的 ApplicationUser 继承自 IdentityUser ,它将使用它现有的属性和关系。它已经定义了一个 Id 列,因此创建 UserId 不是必需的,而且尝试使 UserId 属性。您应该使用现有的 Id 列,默认情况下为 string ,它显示您现有的用户表有一个整数 PK,所以它们不匹配。



然而,这不是世界的尽头。 Identity的内置实现使用 string 作为他们的类( AspNetUsers,AspNetRoles等... )的关键,但是它们允许我们使用另一种类型的主键。



所以...我该怎么办?



身份为每个表定义两个类,所以有两个 IdentityUser 类,两个 IdentityRole 等等。一个是基类泛型类,它接受一些泛型。另一个是该基础的内置实现,它提供了这些通用类型。



这个基类看起来像这样:

  public class IdentityUser 其中TLogin:IdentityUserLogin< TKey> 
其中TRole:IdentityUserRole< TKey>
其中TClaim:IdentityUserClaim< TKey>

第一个通用类型是 TKey ,其中定义键的类型,即 string 。第二个是一个 TLogin ,它是一个 IdentityUserLogin< TKey> ,这意味着它应该是继承 IdentityUserLogin 使用相同的 TKey 类型(可能 string 也许一个 int )。



同时,这些基类的内置实现如下所示: p>

  public class IdentityUser 
:IdentityUser< string,IdentityUserLogin,IdentityUserRole,IdentityUserClaim> IUser,IUser< string>其中将 TKey 定义为
string 用作关键字。像$ IdentityUserLogin IdentityUserRole 的其他泛型是内置的实现基类的类,一个 string 键。看看如何 IdentityUserRole 看起来像:

  public class IdentityUserRole IdentityUserRole<串GT; {} 

看到? string 以及。许多其他类使用 string 作为 TKey 的默认值。



所以如果你这样改变你的类:

  public class ApplicationUser:IdentityUser< int,IdentityUserLogin,IdentityUserRole,IdentityUserClaim> 
{
public virtual User user {get;组; }
}

它还不行,因为我们更改了 TKey int ,但内置的 IdentityUserLogin 仍然使用字符串。因此,我们需要创建自己的 IdentityUserLogin ,如下所示:

  public class MyNiceUserLogin:IdentityUserLogin< int> 
{
}

...并使用如下: p>

  public class ApplicationUser:IdentityUser< int,MyNiceUserLogin,IdentityUserRole,IdentityUserClaim> 
{
public virtual User user {get;组; }
}

...但是我们还要和其他类一样做像 IdentityUserRole IdentityUserClaim



我将其配置为使用 int 。现在,一对一的关系怎么样?



你的代码在这里:

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity< ApplicationUser>()
.HasRequired(i => i.user).WithRequiredDependent(i => i.appUser);
}

...工作正常。您正在以必须的方式进行配置,没有用户 ApplicationUser >,反之亦然。






现在,我建议您阅读这个Stack Overflow答案,以确切地知道要改变Identity的步骤


我也强烈推荐 / strong>你阅读这篇文章 by John Atten,所以你可以深入了解如何自定义/扩展身份。



I have a custom users table in my database and i want to create a one-to-one relationship with aspnetusers table so that when i register a new customer, applicationuser class through UserManager should add the username, email, password and school code to the users table and add an fk in its own table. IS there any tutorial/example implementing this kind of scenario?

I am trying to add a one-to-one relationship with my users table but the fk is different than pk

  • Users Table [columns]
    • username
    • password
    • school code
    • userId [pk]

ASPNETUsers Table [standard columns]

  • Id, [pk]
  • UserName,
  • PasswordHash,
  • SecurityStamp,
  • Discriminator,
  • Email,
  • EmailConfirmed,
  • PhoneNumber,
  • PhoneNumberConfirmed,
  • TwoFactorEnabled,
  • LockoutEndDateUtc,
  • LockoutEnabled,
  • AccessFailedCount
  • userId [fk to users table]

I have added property inside 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; }
}

What else do i need to do and is it correct? How does entityframework work with this one?

I have tried to explain My application structure here:

UPDATE On running it gives me error MySql.Data.MySqlClient.MySqlException: Unknown column 'Extent8.appUser_Id' in 'on clause'

If I define

modelBuilder.Entity<ApplicationUser>().HasKey(u => u.UserID);

inside OnModelCreating then it tries to link AspNetRoles and Claims etc tables through this UserID and ofcourse crashes.

ApplicationUser_Claims_Source_ApplicationUser_Claims_Target: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'UserId' on entity 'IdentityUserClaim' does not match the type of property 'UserID' on entity 'ApplicationUser' in the referential constraint 'ApplicationUser_Claims'. ApplicationUser_Logins_Source_ApplicationUser_Logins_Target: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'UserId' on entity 'IdentityUserLogin' does not match the type of property 'UserID' on entity 'ApplicationUser' in the referential constraint 'ApplicationUser_Logins'. ApplicationUser_Roles_Source_ApplicationUser_Roles_Target: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'UserId' on entity 'IdentityUserRole' does not match the type of property 'UserID' on entity 'ApplicationUser' in the referential constraint 'ApplicationUser_Roles'.

解决方案

How EF's one-to-one relationship works, in short...

When working with one-to-one relationship, Entity Framework uses the parent's Id column in the child as the FK to the parent, it won't create a second column for FK because that would be considered one-to-many relationship, because that second column would allow a parent Id to exist in many child entries, whereas the PK as FK wouldn't.

How should I configure both keys?

Since your ApplicationUser inherits from IdentityUser, it will use it's existing properties and relationships. It already defines an Id column, so creating UserId is not necessary, as well as trying to make UserId the key property. You should use the existing Id column, which by default is string and it looks your existing User table has an integer PK, so they don't match.

However, this is not the end of the world. Identity's built-in implementation uses string as key for their classes (AspNetUsers, AspNetRoles, etc...) but they allows us to use another type of primary key.

So...what should I do?

Identity defines two classes for each table, so there are two IdentityUser classes, two IdentityRole, and so on. One is the base generic class, which accepts some generics. The other is the built-in implementation of that base class, which supplies those generic types.

This base class looks like this:

public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
        where TLogin : IdentityUserLogin<TKey>
        where TRole : IdentityUserRole<TKey>
        where TClaim : IdentityUserClaim<TKey>

The first generic type is TKey, which defines the key's type, which is string. The second is a TLogin, which is a IdentityUserLogin<TKey>, which means it should be some class inheriting IdentityUserLogin using the same TKey type used as key (maybe string, maybe an int).

Meanwhile, the built-in implementation for these base classes looks like this:

public class IdentityUser 
    : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string> {}

Which defines TKey as a string to be used as key. The other generics like IdentityUserLogin and IdentityUserRole, are built-in classes implementing the base classes which a string key. Take a look how IdentityUserRole looks like:

public class IdentityUserRole  IdentityUserRole<string> {}

See? string as well. Many other classes use string as default for TKey.

So if you were to change your class like this:

public class ApplicationUser : IdentityUser<int, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
    public virtual User user { get; set; }
}

It wouldn't work yet, because we changed TKey to be int, but the built-in IdentityUserLogin still uses string. So we need to create our own IdentityUserLogin like this:

public class MyNiceUserLogin : IdentityUserLogin<int>
{
}

...and use it like this:

public class ApplicationUser : IdentityUser<int, MyNiceUserLogin, IdentityUserRole, IdentityUserClaim>
{
    public virtual User user { get; set; }
}

...but we still had to do the same with the other classes like IdentityUserRole and IdentityUserClaim.

I configured it to use int. Now what about one-to-one relationship?

Your code here:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<ApplicationUser>()
      .HasRequired(i => i.user).WithRequiredDependent(i => i.appUser);
}

...works fine. You're configuring in a way both must exist, there is no ApplicationUser without a User and vice-versa.


Now, I recommend you read this Stack Overflow answer to know exactly what steps to follow in order to change Identity's classes to use int as key.

I also strongly recommend you to read this post by John Atten, so you can understand deeply about how to customize/extend Identity.

这篇关于当fk与pk - EntityFramework不同时,如何链接一对一的关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆