36

I have a new ASP.NET MVC 5.1 project using the ASP.Net Identity. It seems reliable and promise, but today i spend almost 9 hours to do a simple things if using SQL.

My problem is, i cannot create a table via CodeFirst with foreign key reference to the default AspNetUsers table.

For example: I have create a table named - Address

   public class Address
    {
        [Key]
        public string UserId { get; set; }
        public string MyAddress { get; set; }
    }

But how can i create a foreign key reference to AspNetUsers?

I try replace the property above by

public IdentityUser MyAddress { get; set; }
// Or
public virtual IdentityUser MyAddress { get; set; }
// Or
public virtual ApplicationUser MyAddress { get; set; }

But all of them still show error:

One or more validation errors were detected during model generation:

MiaPos.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType. 
MiaPos.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType. 
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined. 
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.

I also try override the OnModelCreating from this post

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
    modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
    modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
}

But when i run Add-Migration, it create the IdentityUserLogin, IdentityRole, IdentityUserRole tables and it duplicate, just the prefix is different. (AspNet<->Identity)

Finally i do it by SQL in 10 seconds, but i just want to know why i cannot do in CodeFirst? And why so hard to do a such things from Microsoft 2014 New Technology.

enter image description here

Community
  • 1
  • 1
Cheung
  • 15,293
  • 19
  • 63
  • 93

3 Answers3

29

You can do like this. because sometime you may need 1-1 connection

 public class AnotherTable
    {
        [Key]
        public string UserId { get; set; }

        [ForeignKey("UserId")]
        public virtual ApplicationUser User { get; set; }
    }
Wolf
  • 6,361
  • 2
  • 28
  • 25
  • 7
    this should be the right answer ! for not 1-1 , you create a third table , which contains both AnotherTable ID and ApplicationUser ID – Zakos Oct 12 '14 at 08:08
  • @MichaelMao Simply by convention. Name your child class foreign key string property to 'ApplicationUserId' this will be the foreign key by convention. EntityName + Id. Same is valid for primary key. 'Id' will always be the primarykey. – Pascal Aug 26 '16 at 19:12
28

Thanks you Gábor Plesz for comment above.

I found the solution.

The ApplicationUser class who inherit IdentityUser (which create AspNetUsers table) should create a ICollection property of the child class (child table).

e.g.

public virtual ICollection<ToDo> ToDoes { get; set; }

So the ToDo class can be reference to ApplicationUser

Highly recommend have a look the sample as Gábor Plesz said.

enter image description here

Cheung
  • 15,293
  • 19
  • 63
  • 93
  • 4
    Please mark your answer as the correct answer to help others – Simua Sep 09 '14 at 09:37
  • @Cheung how to add foreign key relationship if my entities are in another class library project and Identity tables are in web project? Please let me know if you have any idea? – Steve Nov 08 '16 at 11:42
  • Would I need to change the IdentityUser to ApplicationUser, whereever IdentityUser was used? – Ajit Goel May 11 '19 at 23:32
2

If you really wanna create a table named Address instead of creating a property named Address to generated as a column into AspNetUsers table do the following (obs: in this case i am assuming that a user can have many address):

  1. At your Address class:

    public class Address
    {
        public string MyAddress { get; set; }
    
        // referencing the AspNetUsers table ( ApplicationUser.cs)
        public string UserId { get; set; }
        public virtual ApplicationUser User { get; set; }
    }
    
  2. Your ApplicationUser Class:

    public virtual ICollection<Address> Addresses { get; set; }
    
  3. Within your DBContext in OnModelCreating method do the following ( fluent API ):

    builder.Entity<Address>()
                  .HasOne(c => c.User)
                  .WithMany(x => x.Addresses)
                  .HasForeignKey(f => f.UserId)
                  .HasConstraintName("UserId")
                  .OnDelete(DeleteBehavior.Cascade)
                  .IsRequired();
    

OBS: Don´t forget adding above the OnModelCreating method this line of code:

public DbSet<Address> Addresses { get; set; }

After it run those commands within your Package Manager Console:

Add-Migrations UserAddress

and after:

Update-Database. 

But, if you wanna just inclued a new property to the AspNet Users just create at your ApplicationUser.cs file/class this property:

public class ApplicationUser : IdentityUser
    {
        public string Address { get; set; }
    }

and run the same commands for your PMC:

Add-Migration UserAddress

after:

Update-Database

Hope this helps!

Sergio Rezende
  • 762
  • 8
  • 25