2

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.

4
  • Can you put up the code for IdentityUserLogin, IdentityUserRole, & DbContext? Commented Jul 11, 2014 at 11:44
  • I think it's a context related issue. I guess you have two contexts. Try merging both. Commented Jul 11, 2014 at 14:21
  • The MVC5 default internet application creates ApplicationUser which inherits from IdentityUser which contains IdentityUserLogin and IdentityUserRole, these are not visible to my project. Commented Jul 11, 2014 at 21:15
  • 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. Commented Jul 11, 2014 at 21:19

5 Answers 5

2

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".

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

I thought EF Code First could automatically identify [Key] if you named property as ID? ApplicationUser already inherits from IdentityUser and one of my contexts inherits from IdentityDbContext (the other from DbContext)
0

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; }
}

2 Comments

By EF convention Id or ID is primary key. No need to explicitly specify that.
Potentially but IdentityUserLogin and IdentityUserRole are clases created by ApplicationUser as it inherits form IdentityUser, how do I add the Key attribute to IdentityUserLogin and IdentityUser role and why wouldn't Microsoft annotate these with [Key] by default?
0

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

5 Comments

Thanks, I am using two DbContexts, one created with the MVC5 Internet Application by default inheriting from IdentityDbContext as per point 1 and the other created via scaffolding inheriting from standard DbContext. Only change that I have made to the wizard created contexts is to point them at the same DefaultConnectionString so all tables are in the same database. Does EF Code First only use the one context at a time to create database? If so then this is probably the answer as database is created bit by bit.
Is it poor architecture to user the ApplicationDbContext : IdentityDbContext for my entire application?
I will start with your second question. It is not poor architecture to use ApplicationDbContext:IdentityDbContext. IdentityDbContext is just a more specialized derived class that adds identity support, if your dbcontext doesn't derive from IdentityDbContext, you will run into more problems that you have not yet, I promise. (Once you start attaching entities to the users and updating the users)
Re: your 1st question, you would want to create a DBContext per OwinContext (which is your request context) after you make sure that you derive from IdentityDbContext.The dbcontext within an HttpRequest must be the same instance to be able to manage entities within that HttpRequest (both user and other entities). I know it sounds confusing but if you look at (blogs.msdn.com/b/webdev/archive/2014/02/12/…), it will make more sense. Also, I recommend the nu-get package for asp.net identity samples.
I think we are almost there. I changed Example to use ApplicationDbContext : IdentityDbContext and then removed OneToManyCascadeDeleteConvention on DbModelBuilder. I still have error with no key defined for IdentityUserLogin and IdentyUserRole though. May take me a little bit of time to digest this blog, thanks will see where this takes me.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.