2
votes

would really appreciate a bit of guidance on how to use ApplicationUser within my own custom class in MVC5.

Simple model, using scaffolding to create controller and view, sm DbContext for the default ApplicationUser (IdentityUser) from the Internet Application template. I want to use the ApplicationUser to stamp the transaction with the details of whoever was logged in at the time.

I get one of two database errors when Entity Framework tries to DropAndRecreateAlways

First my class:

public class Example
{
    public int ID { get; set; }

    [Required]
    public string Description { get; set; }

    [Required]
    public double Amount { get; set; }

    [Required]
    public virtual ApplicationUser CreatedBy {get; set;}

    [Required]
    public virtual ApplicationUser ModifiedBy { get; set; }
}

Errors I get when Entity Framework tries to create the database are:

  1. {"One or more validation errors were detected during model generation:\r\n\r\nMvcApplication1.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.\r\nMvcApplication1.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.\r\nIdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.\r\nIdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.\r\n"}

  2. A referential integrity due to Cascade on Delete (I can override DbContext to remove cascading deletes)

What is the best way to achieve this? Any guidance would be appreciated.

5
Can you put up the code for IdentityUserLogin, IdentityUserRole, & DbContext?bdparrish
I think it's a context related issue. I guess you have two contexts. Try merging both.Ashish Charan
The MVC5 default internet application creates ApplicationUser which inherits from IdentityUser which contains IdentityUserLogin and IdentityUserRole, these are not visible to my project.Chris Beckman
I do have two contexts, ApplicationDbContext is created by default inheriting from IdentityDbContext and then I created one for the rest of my application to use (via scaffolding) which inherits from a standard DbContext. Both contexts point at the default connection string and if I remove ApplicationUser from my class then the Example table gets created in the same database as the default AspNet(Users) tables from the MVC template. I have tried using the default IdentityDbContext for the ApplicationUser class and my example class but get the same error.Chris Beckman

5 Answers

2
votes

Your problem is that your forgot to call

base.OnModelCreating(modelBuilder);

On top off your method since like you said ApplicationDbContext is created by default inheriting from IdentityDbContext. So calling base.OnModelCreating, let you base class ie IdentityDbContext, generate all you need.

But if you still want to use fluent API, the correct way for creating the key for that two entities would be

modelBuilder.Entity<IdentityUserLogin>().HasKey(t => new { t.UserId, t.ProviderKey, t.LoginProvider });
modelBuilder.Entity<IdentityUserRole>().HasKey(t => new { t.RoleId, t.UserId });

Without doing that, calling AddToRole(userid, rolename) method of UserManager will throw an exception requiring the extra key you added with the message "The field UserId or RoleId is required".

1
votes

I'd suggest you to annotate your ID with [Key] attribute. Currently EF can't find the primary key for table Example based upon the first error you've posted.

Also, if you want to lazy load CreatedBy and ModifiedBy in future, you also need to mention Id for that, even though that is not an error for now. It might be easy for future.

Apart from that, also inherit your ApplicationUser class from IdentityUser class and DbContext class from IdentityDbContext class.

Clean the project, rebuild it and see if any error occurs. Then it might be easier to solve it.

0
votes

It is looking for the [Key] annotation for your entities. Looks like you are missing one for both IdentityUserLogin and IdentityUserRole

public class Person
{
    [Key] // <--- this needs to be identified here or in the DbContext you setup
    public Guid ID { get; set; }
}
0
votes

Two suggestions:

1 - Make sure that your dbcontext derives from IdentityDbContext.

2 - Based on your model, you have relationships between the user and your other entities. How does your dbcontext DBSet statements look? Do you have a dbset for your users? (which means you are assuming management of users instead of the IdentityFramework). If you don't have a DBSet for your users, then I would agree with Ashish's statement, the context of Identity entities might be different from the context of your other entities, which will create problems.

If you can post the basics of your context class, we might be able to tell more

0
votes

I've decided to post an answer that works, however I remain unconvinced it's the cleanest answer and would love to hear if anyone has a better way of doing this.

The answer for me to get this to work was to override OnModelCreating() in ApplicationDbContext: IdentityDbContextand use Fluent API to mark properties of IdentityUserLogin and IdentityUserRole as [Key]

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

        modelBuilder.Entity<IdentityUserLogin>().HasKey(t => t.UserId);
        modelBuilder.Entity<IdentityUserRole>().HasKey(t => t.UserId);

    }

I don't think this is a very good solution as EF Code First now creates two columns in my IdentityUserLogin and IdentityUserRole tables, UserId and User_Id.

This will do for now. Suggestions are VERY welcome.