Many to many relationship on same class not working

c# entity-framework-core many-to-many

Question

I am trying to create a many to many relationship using EFCore on the same user class which I have based on EntityFrameworkCore.IdentityUser following the instructions described here, here and here. I have the following user class:

public class MyApplicationUser : IdentityUser
{
    public virtual ICollection<MyApplicationUserJunction> MyApplicationUsers { get; set; }
    public virtual ICollection<MyApplicationUserJunction> ManagingMyApplicationUsers { get; set; }
}

Here is my joining table:

public class MyApplicationUserJunction
{
    public int MyApplicationUserId { get; set; }
    public virtual MyApplicationUser MyApplicationUser { get; set; }
    public int ManagedMyApplicationUserId { get; set; }
    public virtual MyApplicationUser ManagedMyApplicationUser { get; set; }
}

Here is my OnModelConfiguring:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<MyApplicationUserJunction>()
        .HasKey(uj => new { uj.ManagedMyApplicationUserId, uj.MyApplicationUserId});

    builder.Entity<MyApplicationUserJunction>()
        .HasOne(uj => uj.MyApplicationUser)
        .WithMany(qu => qu.MyApplicationUsers)
        .HasForeignKey(uj => uj.MyApplicationUserId).OnDelete(DeleteBehavior.Restrict);

    builder.Entity<MyApplicationUserJunction>()
        .HasOne(uj => uj.ManagedMyApplicationUser)
        .WithMany(qu => qu.ManagingMyApplicationUsers)
        .HasForeignKey(uj => uj.ManagedMyApplicationUserId);
}

Whenever I run the EFCore tools add migrations command, I get the following error:

The relationship from 'MyApplicationUserJunction.MyApplicationUser' to 'MyApplicationUser.MyApplicationUsers' with foreign key properties {'MyApplicationUserId' : int} cannot target the primary key {'Id' : string} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.

I am using the EntityFrameworkCore.Tools.DotNet package 1.0.0-preview3-final.

I have tried only using a single navigation property on the User class, and I've tried not specifying the WithMany function as described here but with no success.

1
2
9/10/2017 9:50:19 PM

Accepted Answer

To get this working with EF Core 1, following Sir Rufo's suggestion above I specified the type of the TKey:

public class MyApplicationUser : IdentityUser<int>
{
    public virtual ICollection<MyApplicationUserJunction> MyApplicationUsers { get; set; }
    public virtual ICollection<MyApplicationUserJunction> ManagingMyApplicationUsers { get; set; }
}

and in the context:

public class MyApplicationContext : IdentityDbContext<MyApplicationUser, IdentityRole<int>, int>
{
    ...

and in the AddIdentity section in ConfigureServices which also uses the generic IdentityRole:

services.AddIdentity<MyApplicationUser, IdentityRole<int>>(config =>
        {
            config.User.RequireUniqueEmail = true;
            config.Password.RequiredLength = 8;
            config.Cookies.ApplicationCookie.LoginPath = "/Auth/Login";
        })
        .AddEntityFrameworkStores<MyApplicationContext, int>();

I then upgraded this to EF Core 2 which require some other alterations to get the Identity working. As described by pealmeid here you have to derive from the IdentityRole:

public class MyRole : IdentityRole<int>
{
    public MyRole() : base()
    {
    }

    public MyRole(string roleName)
    {
        Name = roleName;
    }
}

Used in ConfigureServices:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddIdentity<MyApplicationUser, MyRole>(config =>
            {
                config.User.RequireUniqueEmail = true;
                config.Password.RequiredLength = 8;
            }).AddEntityFrameworkStores<MyApplicationContext>();
            ...

I was also using cookie authentication before which is no longer configured in the AddIdentity config lambda but is instead done at the services level:

public void ConfigureServices(IServiceCollection services)
    {
        services.ConfigureApplicationCookie(options => options.LoginPath = "/Auth/Login");
        ...

Also a slight change to the DbContext signature to use the derived role type:

public class MyApplicationContext : IdentityDbContext<MyApplicationUser, MyRole, int>
{
2
12/15/2017 1:26:34 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow