简体   繁体   中英

Extending MVC5 and Entity Framework 6 ApplicationUser causing invalid column error on user registration

I'm trying to extend the ApplicationUser class so that I can contain UserProfile information in a separate table. However I'm having trouble getting this to work and am getting an exception when I call the UserManager.CreateAsync method in my AccountController.

Invalid column name 'UserProfileId'.

My Application User class

public class ApplicationUser : IdentityUser
{
    [ForeignKey("UserProfileId")]
    public virtual UserProfile UserProfile { get; set; }
    public int UserProfileId { get; set; }
}

My User Profile class

public class UserProfile
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required, MaxLength(200)]
    public string EmailAddress { get; set; }

    [Required, MaxLength(50)]
    public string FirstName { get; set; }

    [Required, MaxLength(50)]
    public string LastName { get; set; }
}

My first entity generated code migration

CreateTable(
    "dbo.AspNetUsers",
    c => new
        {
            Id = c.String(nullable: false, maxLength: 128),
            UserName = c.String(),
            PasswordHash = c.String(),
            SecurityStamp = c.String(),
            UserProfileId = c.Int(),
            Discriminator = c.String(nullable: false, maxLength: 128),
        })
    .PrimaryKey(t => t.Id)
    .ForeignKey("dbo.UserProfile", t => t.UserProfileId, cascadeDelete: true)
    .Index(t => t.UserProfileId);

My code to create the new user on Registration

var user = Mapper.Map<ApplicationUser>(model);
var result = await UserManager.CreateAsync(user, model.Password);

With Automapping config of

Mapper.CreateMap<RegisterViewModel, ApplicationUser>()
    .ForMember(dest => dest.UserProfile, opt => opt.MapFrom(v => Mapper.Map<RegisterViewModel, Data.Model.UserProfile>(v)));

Mapper.CreateMap<RegisterViewModel, Data.Model.UserProfile>()
    .ForMember(dest => dest.FirstName, opt => opt.MapFrom(v => v.FirstName))
    .ForMember(dest => dest.LastName, opt => opt.MapFrom(v => v.LastName))
    .ForMember(dest => dest.EmailAddress, opt => opt.MapFrom(v => v.EmailAddress));

NOTE: I have tried removing the int UserProfileId and this instead created a field called UserProfile_Id which caused issues in and of itself.

Solved it. The problem was nothing to do with anything posted in my question. It ended up being because of my use of Dependency Injection causing the wrong DBContext being created and inserted into the AccountController.

What appears to be happening was because I did not have a DbContext mapped in my AutoFaq setup, the constructor used to create the UserStore was it's default. And in this case it appears that means creating a IdentityContext rather than my application DataContext class. However this context did not have the new UserProfile property hence it error'd out on UserProfileId.

I fixed this by adding a specific constructor parameter to my AutoFaq setup.

        builder.RegisterType<Data.Model.DataContext>()
            .As<IDataContext>()
            .InstancePerHttpRequest();

        // Added this here to ensure the context passed in is my DataContext
        builder.RegisterType<UserStore<ApplicationUser>>()
               .As<IUserStore<ApplicationUser>>()
               .WithParameter((pi, ctx) => { return pi.Name == "context"; },
                              (pi, ctx) => { return ctx.Resolve<IDataContext>(); }
                          );

        builder.RegisterType<UserManager<ApplicationUser>>().As<UserManager<ApplicationUser>>();    

Just another possibility to save those who, like me had the correct constructor being hit. I had a method to create seed data in the database registered in my startup.cs (Core 2) this was querying the table I was trying to extend and caused the same error. Made sense when I found it I guess. Commenting out the lines while I ran add-migration was enough to get me past the issue.

Thanks, the accepted answer helped me. For anyone using Ninject, this is the code that I ended up with

Bind<IUserStore<ApplicationUser>>().ToMethod(
   ctx =>
   {
     var dbContext = ApplicationDbContext.Create();
     return new UserStore<ApplicationUser>(dbContext);
   }
);

Bind<UserManager<ApplicationUser>>().ToSelf();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM