Foreign Key To Microsoft.AspNet.Identity.EntityFramework.IdentityUser?

15,662

Solution 1

It is easy to create a one-to-many relationship between ApplicationUser and MyObject and add a "UserId" foreign key in your MyObjects table. What I like about this solution is that it follows EF conventions and there is no need for [ForeignKey] attribute in your model:

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<MyObject> MyObjects { get; set; }
}

public class MyObject
{
    public int MyObjectId { get; set; }

    public string MyObjectName { get; set; }

    // other properties

    public virtual ApplicationUser ApplicationUser { get; set; }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public DbSet<MyObject> MyObjects { get; set; }

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

        modelBuilder.Entity<MyObject>()
            .HasRequired(c => c.ApplicationUser)
            .WithMany(t => t.MyObjects)
            .Map(m => m.MapKey("UserId"));
    }
}

Notice the use of Fluent API to create a "UserId" foreign key in your MyObjects table. This solution would still work without adding the Fluent API, but then your foreign key column would be named "ApplicationUser_Id" in your MyObjects table by convention.

Solution 2

I would do the following: In the ApplicationUser class, add a ForeignKey attribute,

public ApplicationUser : IdentityUser {
    [ForeignKey("UserID")]
    public virtual ICollection<MyCustomUser> MyCustomUsers{ get; set; }    
}

and in your model where you want to track to which user it belongs,

public MyObject {
    public string UserId { get; set; }

    // other properties
}

You don't need to store the whole ApplicationUser instance in the MyObject class, and the UserID will be generated automatically. It is important that is is of type string, as is the ID of the ApplicationUser!

Solution 3

public MyObject
{
   .. other properties

   [MaxLength(128), ForeignKey("ApplicationUser")]
   public virtual string UserId { get; set; }

   public virtual ApplicationUser ApplicationUser { get; set;}
}
Share:
15,662
Mickael Caruso
Author by

Mickael Caruso

Enthusiast Programmer working in mostly C#, the .net Framework, SQL Server, Flex/Actionscript, and (new to) Android.

Updated on June 04, 2022

Comments

  • Mickael Caruso
    Mickael Caruso almost 2 years

    I'm in VS 2013 and have just created an MVC application.

    I'm creating an object I intend to have a foreign key to the AspNetUsers table in the resulting database. The project does have an ApplicationUser (deriving from IdentityUser) that looks like a property-column match with the AspNetUsers table.

    How do we properly declare a foreign key to this?

    public MyObject
    {
       public string UserId { get; set; }
    
       [ForeignKey("UserId")]
       public ApplicationUser User { get; set;}
    
       // other properties
    }
    

    Now, I modify ApplicationUser to have a collection of MyObjects:

    public ApplicationUser : IdentityUser
    {
         public virtual ICollection<MyObject> MyObjects { get; set; }    
    }
    

    This seems to be how to do one-to-many in EF Code First. However, when I update-database, I'm getting the errors that say Identity members (IdentityUserLogin, IdentityUserRole, etc.) have no keys defined. Perhaps those classes were not meant to participate in EF Code First Migrations?

    I could go "to the back" and add the foreign key via SQL statements, but if I wanted to update again from Code First, I might get errors (that the database doesn't currently match the older migration or something like that).

    How do we properly foreign-key reference those membership tables?

    I also tried to create an AspNetUser class with matching properties of the AspNetUsers table. Instead of "public ApplicationUser" on the Client class, I declared "public AspNetUser". Doing this resulted in a migration failure - "Automatic migration was not applied because it would result in data loss."

    So, what to do?

  • Mickael Caruso
    Mickael Caruso over 10 years
    I'm not trying to inherit MyCustomUser. I'm trying to have MyObject to have a foreign key to the ApplicationUser.
  • Mickael Caruso
    Mickael Caruso over 10 years
    I tried to inherit IdentityDbContext<ApplicationUser> - doesn't work. I still get the same error when I try to migrate.
  • Olav Nybø
    Olav Nybø over 10 years
    Updated my answer with some information on what to do depending on which error you get after making your DbContext inherit from the IdentityDbContext<ApplicationUser> class
  • Mickael Caruso
    Mickael Caruso over 10 years
    I got the error ".. result in lost data" when I created the AspNetUser class manually and included it in the migration. I also got that error when I overrode OnModelCreating with the 3 FK statements without inheriting IdentityDbContext<TUser>. When I inherited IdentityDbContext<TUser> as documentation states to do, I still get those "no keys defined" errors - I thought that IdentityDbContext<TUser> declared those keys, which would take care of everything.
  • Mickael Caruso
    Mickael Caruso over 10 years
    Interestingly, I just tried to start a new MVC project. The first thing I did was run Update-Database in the PM console. So I didn't create any new POCO objects or edit any existing models the project generated. I know there's the ApplicationDbContext already in there which inherited IdentityDbContext<TUser>. Running Update-Database gave me those validation errors where "has no keys defined." Microsoft may have taken those FK attributes out recently.
  • Cheung
    Cheung about 10 years
    I also try all possible solution of StackOverFlow, i also cannot set foreign key to the AspNetUsers table by Code-First, the new ASP.NET Identity is a joke.
  • ToastyMallows
    ToastyMallows about 9 years
    What is the reason for the last line, public virtual ApplicationUser ApplicationUser { get; set; }? Shouldn't the ForeignKey to ApplicationUser in the line above that be sufficient?
  • MichaelMao
    MichaelMao almost 8 years
    How to achieve this by Fluent API?
  • kimbaudi
    kimbaudi over 7 years
    @MichaelMao - Please take a look at my solution on how to achieve this using Fluent API. Actually, you don't need Fluent API if you're happy with the foreign key name defaulting to "ApplicationUser_Id" by EF convention. However, if you want to customize the name of the foreign key, then it is necessary.
  • kimbaudi
    kimbaudi over 7 years
    I prefer to avoid using attributes to define EF relations as @BlackICE suggests and would avoid using [ForeignKey]. My solution uses Fluent API to define the relation, which I feel is much cleaner. Of course, the Fluent API is unnecessary if you want the foreign key to be named "ApplicationUser_Id" by convention.
  • Daniel
    Daniel almost 4 years
    @ToastyMallows, the last line gives me a navigation property in linq queries. But you are right, leave it out and you will still get the foreign key.