Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a foriegn key using ApplicationUser (Identity) fails #7288

Closed
Biarity opened this issue Dec 21, 2016 · 19 comments
Closed

Adding a foriegn key using ApplicationUser (Identity) fails #7288

Biarity opened this issue Dec 21, 2016 · 19 comments
Assignees

Comments

@Biarity
Copy link

Biarity commented Dec 21, 2016

I want to add a foreign key to one of my business models (a DbSet) in order to associate it with a user.

To do that I added the following code to my model (using ApplicationUser as discovery):

public string UserId { get; set; }
[ForeignKey("UserId")]
public ApplicationUser User { get; set; }

ApplicationUser is the ASP.NET Core Identity user model that's created by default in VS when you select individual user authentication for a project.

Running Update-Database gives the following error: The entity type 'IdentityUserLogin<string>' requires a primary key to be defined.. However, looking at the mentioned class, I see it has a UserId property, which should be considered a primary key (also the generated AspNetUsers table has an Id field). To make it clearer that it has a primary key, I added the following code to ApplicationDbContext.OnModelCreating: builder.Entity<ApplicationUser>().HasKey(u => u.Id);. I still get the same error.

Notes

I am using Npgsql & a remote psotgres database. I also have IdentityServer 4 working with the Identity system.

Further technical details

EF Core version: 1.1.0
Database Provider: Npgsql.EntityFrameworkCore.PostgreSQL
Operating system: Windows
IDE: same issue in VS2015 & VS2017 RC2

@divega divega added this to the 2.0.0 milestone Jan 6, 2017
@divega
Copy link
Contributor

divega commented Jan 6, 2017

@HaoK can you help with a repro here?

@HaoK
Copy link
Member

HaoK commented Jan 9, 2017

@Biarity the UserId in IdentityUserLogin is the foreign key for the ApplicationUser, the key for the UserLogin is defined as: b.HasKey(l => new { l.LoginProvider, l.ProviderKey }); if you want to match how we do it by default in our EF DbContext:

@tbaggett
Copy link

FYI, I've also run into this exact problem. I am also using EF Core 1.1 and Npgsql/postgres, but working on a Mac with VS Code. I will try to create a repro to demonstrate the issue.

@tbaggett
Copy link

Repro of issue available at https://github.com/tbaggett/EFIssue7288Repro. I followed these steps to create the project:

  • Created the project using "yo aspnet" with "Web Application" type selected
  • Ran dotnet restore
  • Ran dotnet build
  • Ran dotnet ef database update
  • Added "TestContext" and "TestModel" classes
  • Added a call to services.AddDbContext in the Startup's ConfigureServices method to add the new context
  • Ran dotnet ef migrations add AddedTestContext --context TestContext
  • Received "The entity type 'IdentityUserLogin' requires a primary key to be defined." error message (full output below)

Note that this example is using Sqlite as implemented by default. Using PostgreSQL appears to be unrelated to the problem.

Full error output:

bash-3.2$ dotnet ef migrations add AddedTestContext --context TestContext
Project EFIssue7288Repro (.NETCoreApp,Version=v1.0) will be compiled because project is not safe for i
ncremental compilation. Use --build-profile flag for more information.
Compiling EFIssue7288Repro for .NETCoreApp,Version=v1.0
Bundling with configuration from /Users/tommybaggett/Projects/ASP_NET_MVC_CORE/EFIssue7288Repro/bundle
config.json
Processing wwwroot/css/site.min.css
Processing wwwroot/js/site.min.js
Compilation succeeded.
0 Warning(s)
0 Error(s)
Time elapsed 00:00:03.3071053
(The compilation time can be improved. Run "dotnet build --build-profile" for more information)
System.InvalidOperationException: The entity type 'IdentityUserLogin' requires a primary key t
o be defined.
at Microsoft.EntityFrameworkCore.Internal.ModelValidator.ShowError(String message)
at Microsoft.EntityFrameworkCore.Internal.ModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Internal.RelationalModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConvent
ionSetBuilder conventionSetBuilder, IModelValidator validator)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.LazyRef1.get_Value() at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(Scope dCallSite scopedCallSite, ServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass16_0.<RealizeService>b __0(ServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IService Provider provider) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitTransient(Tr ansientCallSite transientCallSite, ServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor( ConstructorCallSite constructorCallSite, ServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitTransient(Tr ansientCallSite transientCallSite, ServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass16_0.<RealizeService>b __0(ServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(ISe rviceProvider provider, Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T]( IServiceProvider provider) at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, Str ing outputDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outp utDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_01.b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The entity type 'IdentityUserLogin' requires a primary key to be defined.
bash-3.2$

@HaoK
Copy link
Member

HaoK commented Feb 10, 2017

Unassigning so triage can pick this up now that there's a repro

@HaoK HaoK removed their assignment Feb 10, 2017
@bricelam bricelam removed this from the 2.0.0 milestone Feb 21, 2017
@rowanmiller rowanmiller added this to the 2.0.0 milestone Feb 22, 2017
@rowanmiller
Copy link
Contributor

@HaoK can you isolate whether there is an EF bug here or not.

@smithaitufe
Copy link

I am also having the same issue

I am using Ubuntu 16.04 LTS, VS Code, Core 1.1 and Postgresql (Npsql)

I have two contexts: LibrilleIdentityContext and LibrilleContext.
When I run the dotnet ef migrations add InitialCreate -c LibrilleIdentityContext -o "Migrations/Identity" code below, it creates the necessary migration files for users, roles, userlogins, userclaims, usertokens.

However, when I run dotnet ef migrations add InitialCreate -c LibrilleContext -o "Migrations", it throws an error.

W
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, St
ring contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0
()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The entity type 'IdentityUserLogin<int>' requires a primary key to be defined.

The line is

The entity type 'IdentityUserLogin<int>' requires a primary key to be defined.

@HaoK
Copy link
Member

HaoK commented Mar 28, 2017

What's the difference between your two context's @smithaitufe

@smithaitufe
Copy link

smithaitufe commented Mar 28, 2017

@HaoK Thanks for reaching out

LibrilleIdentityContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Librille.Models;

namespace Librille.Data
{
    public class LibrilleIdentityContext : IdentityDbContext<User, Role, int>
    {

        public LibrilleIdentityContext(DbContextOptions<LibrilleIdentityContext> options)
            : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder builder)
        {
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
            builder.Entity<User>(i =>
            {
                i.ToTable("Users");
                i.HasKey(x => x.Id);
            });
            builder.Entity<Role>(i =>
            {
                i.ToTable("Roles");
                i.HasKey(x => x.Id);
            });
            builder.Entity<IdentityUserRole<int>>(i =>
            {
                i.ToTable("UserRoles");
                i.HasKey(x => new { x.RoleId, x.UserId });
            });
            builder.Entity<IdentityUserLogin<int>>(i =>
            {
                i.ToTable("UserLogins");
                // i.HasKey
                i.HasIndex(x => new { x.ProviderKey, x.LoginProvider });
            });
            builder.Entity<IdentityRoleClaim<int>>(i =>
            {
                i.ToTable("RoleClaims");
                i.HasKey(x => x.Id);
            });
            builder.Entity<IdentityUserClaim<int>>(i =>
            {
                i.ToTable("UserClaims");
                i.HasKey(x => x.Id);
            });
            builder.Entity<IdentityUserToken<int>>(i => {
                i.ToTable("UserTokens");
                i.HasKey(x => x.UserId);
            });

            base.OnModelCreating(builder);
        }
    }
}

and my

LibrilleContext.cs

using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Librille.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;

namespace Librille.Data
{
    // Change to LibrilleContext: DbContext when using two db contexts. Add IdentityDbContext<User, Role, int> to LibrilleIdentityContext
    public class LibrilleContext : DbContext
    {
        public DbSet<TermSet> TermSets { get; set; }
        public DbSet<Term> Terms { get; set; }
        public DbSet<Image> Images { get; set; }
        public DbSet<Publisher> Publishers { get; set; }
        public DbSet<Author> Authors { get; set; }
        public DbSet<Book> Books { get; set; }
        public DbSet<BookImage> BookImages { get; set; }
        public DbSet<BookVariant> BookVariants { get; set; }
        public DbSet<BookAuthor> BookAuthors { get; set; }
        public DbSet<BookVolume> BookVolumes { get; set; }
        public DbSet<BookEdition> BookEditions { get; set; }
        public DbSet<BookVersion> BookVersions { get; set; }
        public DbSet<CheckOut> CheckOuts { get; set; }
        
        public DbSet<Recall> Recalls { get; set; }
        public DbSet<Review> Reviews { get; set; }

        public DbSet<Post> Posts { get; set; }
        public DbSet<Comment> Comments { get; set; }
        public DbSet<Club> Clubs { get; set; }
        public DbSet<ClubGenre> ClubGenres { get; set; }
        public DbSet<Member> Members { get; set; }
        public DbSet<PriceOffer> PriceOffers { get; set; }
        public LibrilleContext(DbContextOptions<LibrilleContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {      
             builder
            .Entity<BookAuthor>()
            .HasIndex(x => new { x.BookId, x.AuthorId }).IsUnique(true);            
            
            builder
            .Entity<BookVariant>()
            .HasMany(bv => bv.CheckOuts).WithOne(co => co.BookVariant);


        }
        // public override int SaveChanges()
        // {
        //     this.ChangeTracker.DetectChanges();

        //     var newEntries = this.ChangeTracker.Entries()
        //         .Where(e => e.State == EntityState.Added);

        //     var updatedEntries = this.ChangeTracker.Entries()
        //         .Where(e => e.State == EntityState.Modified);
                
        //     foreach (var entry in newEntries)
        //     {
        //         entry.Property("InsertedAt").CurrentValue = DateTime.UtcNow;
        //     }
        //     foreach (var entry in updatedEntries)
        //     {
        //         entry.Property("UpdatedAt").CurrentValue = DateTime.UtcNow;
        //     }

        //     return base.SaveChanges();
        // }
    }
}

They are both using the same connection string.

@HaoK
Copy link
Member

HaoK commented Mar 28, 2017

Try moving your base.OnModelCreating call up, see:

// Add your customizations after calling base.OnModelCreating(builder);

@smithaitufe
Copy link

@HaoK
I actually did that but I did not see any difference.

@HaoK
Copy link
Member

HaoK commented Mar 28, 2017

Perhaps the issue is the fact that there are two DbContexts? Is there any way you can try things temporarily with a single db context just to see if that might be causing issues?

@smithaitufe
Copy link

I did and I did not have any issue.

@HaoK
Copy link
Member

HaoK commented Mar 28, 2017

So this issue only occurs with two different DbContext types? @ajcvickers @divega are there any limitations today with using migrations and multiple db contexts?

@ajcvickers
Copy link
Member

@HaoK Yes, in particular when using overlapping models. See #2725

@HaoK
Copy link
Member

HaoK commented Mar 29, 2017

Okay, so can I close this as dupe #2725 since this error doesn't occur if only a single DbContext is involved

@ajcvickers
Copy link
Member

Yes, because TestModel references ApplicationUser, thereby creating two overlapping models.

@ajcvickers ajcvickers removed this from the 2.0.0 milestone Mar 29, 2017
@tmacharia
Copy link

Was this problem solved?

@ajcvickers
Copy link
Member

@devtimmy This was resolved as a duplicate of #2725, which is in the backlog.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants