Question

I am using Entity Framework 6 with an existing database and have migrated some data from an old custom authentication system.

I have two models, one extending the scaffolded ApplicationUser (Identity) in MVC5, and the other a model of an old table. These tables have a 1 to 1 relationship.

Because the UserId for my authentication table used to be an int, and ASP.NET Identity 2 defines the ID as a guid, I have created the association using the old UserId (which is the primary key of tbl0102User).

So tables are:

AspNetUsers:

- Id (guid)
- Username etc
- UserId (int) - this is the column I have created on the table to map to the old User table

Tbl01012Users:

 - UserId (int)
 - address etc...

My code for the two models is:

 public class ApplicationUser : IdentityUser
    {
        public int UserId { get; set; }

        [ForeignKey("UserId")]
        public UserDetails Details { get; set; }
    }

and

[Table("tbl0102User")]
public class UserDetails
{
    // This numeric id contains the relationship between the old authentication system and the new ASP.NET Identity.
    // The new system has a Guid as Id, but that is different to the UserId.
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }

    public ApplicationUser AppUser { get; set; }

    public string UserLogin { get { return AppUser.UserName; } }

    // etc....
}

When I try and build and run the application, I get the following error:

System.InvalidOperationException: Unable to determine the principal end of an association between the types 'Plus.Models.ApplicationUser' and 'Plus.Models.UserDetails'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
   at System.Data.Entity.ModelConfiguration.Edm.Services.AssociationTypeMappingGenerator.GenerateIndependentAssociationType(AssociationType associationType, DbDatabaseMapping databaseMapping)
   at System.Data.Entity.ModelConfiguration.Edm.Services.DatabaseMappingGenerator.GenerateAssociationTypes(DbDatabaseMapping databaseMapping)
   at System.Data.Entity.ModelConfiguration.Edm.Services.DatabaseMappingGenerator.Generate(EdmModel conceptualModel)
   at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
   at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
   at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
   at System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.LazyInternalContext.get_CodeFirstModel()
   at System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(DbContext context, XmlWriter writer)
   at System.Data.Entity.Utilities.DbContextExtensions.<>c__DisplayClass1.<GetModel>b__0(XmlWriter w)
   at System.Data.Entity.Utilities.DbContextExtensions.GetModel(Action`1 writeXml)
   at System.Data.Entity.Utilities.DbContextExtensions.GetModel(DbContext context)
   at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext)
   at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
   at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.GetMigrator()
   at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force)
   at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()
   at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)

Why is this? I have specified on the ApplicationUser object that the foreign key for the Details property maps according to the foreign key userId on the UserDetails object.

How can I change this to work?

I have tried with fluent mapping also, as per: http://msdn.microsoft.com/en-au/data/jj713564.aspx.

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

            modelBuilder.Entity<UserDetails>()
                .HasOptional(t => t.AppUser)
                .WithRequired(t => t.Details)
                ;            
        }

But get the error:

UserDetails_AppUser_Target: : Multiplicity is not valid in Role 'UserDetails_AppUser_Target' in relationship 'UserDetails_AppUser'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.

I also tried:

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

            modelBuilder.Entity<UserDetails>()
                .HasRequired(x=>x.AppUser)
                .WithRequiredDependent(y=>y.Details)               
                ;            
        }

But, I get the error:

UserDetails_AppUser_Target_UserDetails_AppUser_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'UserId' on entity 'UserDetails' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'UserDetails_AppUser'.

This to me says that it is not correctly picking up the foreign key on ApplicationUser, instead using the primary key ID (guid).

Was it helpful?

Solution

I found the answer to this:

public class ApplicationUser : IdentityUser
{
    public int UserId { get; set; }

    [ForeignKey("UserId")]
    public virtual UserDetails Details { get; set; }
}

I'm not sure how this is different to my original, except that I marked the UserDetails object as virtual.

OTHER TIPS

You could try specifing the foreign key like following:

public class ApplicationUser : IdentityUser
{
    [ForeignKey("UserDetails")]
    public int UserId { get; set; }

    public UserDetails Details { get; set; }
}

Another option may be to use the Required attribute on the ApplicationUser property in UserDetails.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top