11

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?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Mickael Caruso
  • 8,721
  • 11
  • 40
  • 72
  • 2
    I would make a suggestion, avoid using the attributes to define your Entityframework relations. If you ever plan on using these object outside of a database application the attributes make it difficult to do. – BlackICE Nov 20 '13 at 18:49
  • I've got your same issue, have you solved it? – Carlo Luther Sep 25 '15 at 13:22

6 Answers6

5

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.

kimbaudi
  • 13,655
  • 9
  • 62
  • 74
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!

EluciusFTW
  • 2,565
  • 6
  • 42
  • 59
  • 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. – kimbaudi Oct 12 '16 at 23:11
1
public MyObject
{
   .. other properties

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

   public virtual ApplicationUser ApplicationUser { get; set;}
}
Daniel
  • 1,044
  • 11
  • 10
  • 1
    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? – ToastyMallows Mar 02 '15 at 02:54
  • 1
    @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. – Daniel Jun 25 '20 at 07:14
1

this is also an alternative way for @kimbaudi suggestion. With this, you don't need to define mapping key attribute instead you can use your own defined Model attribute.

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


   public ApplicationUser User { get; set; }

    // other properties
}

public class ApplicationUser : IdentityUser
{ 
    public virtual ICollection<MyObject> MyObjects { 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(po => po.User)
        .WithMany(a => a.MyObjects)
        .HasForeignKey(po => po.UserId);
    }
}
PNP
  • 361
  • 5
  • 17
0

The ASP.NET Identity classes doesn't use attributes to define the relations, they expect the relations to be configured by the DbContext.

The default DbContext used with ASP.NET Identity, IdentityDbContext<TUser> includes the configuration. So if you make your DbContext class inherit from IdentityDbContext<ApplicationUser> you should be all set.

Update

If you still get error: "Automatic migration was not applied because it would result in data loss." then do:

get Update-Database -Force

If you still get error messages about IdentityUserLogin, IdentityUserRole, etc. have no keys defined then you most likely are overriding the OnModelCreating method in your DbContext without calling the base method first. Add a call to the base like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelbuilder);
    // your code here
}

End of Update

If you don't want to inherit from IdentityDbContext you need to add some configuration for the Identity classes. One way to do this is by overriding OnModelCreating. This is where IdentityDbContext<ApplicationUser> configure the entity framework mappings. If you add this to your DbContext class it should set up the mappings you need.

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 });
}
Olav Nybø
  • 11,454
  • 8
  • 42
  • 34
  • 1
    I tried to inherit IdentityDbContext - doesn't work. I still get the same error when I try to migrate. – Mickael Caruso Nov 20 '13 at 22:29
  • Updated my answer with some information on what to do depending on which error you get after making your DbContext inherit from the IdentityDbContext class – Olav Nybø Nov 21 '13 at 07:12
  • 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. When I inherited IdentityDbContext as documentation states to do, I still get those "no keys defined" errors - I thought that IdentityDbContext declared those keys, which would take care of everything. – Mickael Caruso Nov 21 '13 at 15:05
  • 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. Running Update-Database gave me those validation errors where "has no keys defined." Microsoft may have taken those FK attributes out recently. – Mickael Caruso Nov 21 '13 at 15:16
  • 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. – Cheung Mar 10 '14 at 08:04
0

I think you have it backwards. Maybe try something like:

public MyCustomUser : IUser
{
   public string Id { get; set; }

   public string FavoriteHairColor { get; set;}
}

public ApplicationUser : IdentityUser
{
     public virtual ICollection<MyCustomUser> MyCustomUsers{ get; set; }    
}

I'm going from memory here so I might be a little off. Anyway, the important things is to have your EF user class inherit from IUser.

vidalsasoon
  • 4,365
  • 1
  • 32
  • 40
  • 1
    I'm not trying to inherit MyCustomUser. I'm trying to have MyObject to have a foreign key to the ApplicationUser. – Mickael Caruso Nov 20 '13 at 21:20
  • How to achieve this by Fluent API? – MichaelMao Apr 30 '16 at 14:11
  • @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 Oct 12 '16 at 23:08