How to link one-to-one relationship when fk is different from pk - EntityFramework

后端 未结 1 683
滥情空心
滥情空心 2021-01-26 19:30

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 th

相关标签:
1条回答
  • 2021-01-26 19:56

    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.

    0 讨论(0)
提交回复
热议问题