问题
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'.
回答1:
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.
来源:https://stackoverflow.com/questions/42617017/how-to-link-one-to-one-relationship-when-fk-is-different-from-pk-entityframewo