entity-framework-coreasp.net-core-webapi.net-8.0asp.net-core-identity

Entity Framework Core migration never completes for identity integration into ASP.NET Core Web API project


I have an ASP.NET Core 8.0 Web API project that uses Entity Framework Core.

I got a basic CRUD project working properly with my endpoints delivering data from the database.

I then added ASP.NET Core Identity to my project and I'm trying to get that working. Initially I did an in memory Identity setup and I verified that was working and forcing authentication for my endpoints.

I then tried to get ASP.NET Core Identity working from the database but when I perform the migration it never completes.

Here is my custom role class

public class ShopRole : IdentityRole<int>
{
    public ShopRole() { }
    public ShopRole(string roleName) : base(roleName) { }
}

Here is my custom User class:

public class ShopUser : IdentityUser<int>
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string? DisplayName { get; set; }
    public string? ProfileUrl { get; set; }
    public string? Handle { get; set; }
    public int? RefId { get; set; }
    public string RefIdStr { get; set; } = Guid.NewGuid().ToString();
    public bool IsArchived { get; set; }
    public DateTime? ArchivedDate { get; set; }
    public string? LastLoginIp { get; set; }
    public DateTime? LastLoginDate { get; set; }
    public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
    public DateTime ModifiedDate { get; set; } = DateTime.UtcNow;
}

Here is my DbContext class:

public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : IdentityDbContext<ShopUser, ShopRole, int>(options)
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.Entity<ShopUser>();
    }
}

Here is my database context class for everything else that is not identity related:

public class LibraryContext : DbContext
{
    public LibraryContext(DbContextOptions<LibraryContext> options) : base(options)
    {
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get ; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddJsonFile("appsettings.json")
            .Build();
    
        optionsBuilder.UseNpgsql(configuration.GetConnectionString("DefaultConnection"));
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //configure the Product table
        modelBuilder.Entity<Product>().ToTable("Product");

        modelBuilder.Entity<Product>()
            .HasKey(b => b.Id);

        modelBuilder.Entity<Product>()
            .Property(i => i.Name)
            .HasMaxLength(100)
            .IsRequired();

        modelBuilder.Entity<Product>()
            .HasOne(b => b.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(b => b.CategoryId)
            .IsRequired();

        //configure the category table
        modelBuilder.Entity<Category>().ToTable("Category");

        modelBuilder.Entity<Category>()
            .HasKey(c => c.Id);

        modelBuilder.Entity<Category>()
            .Property(c => c.Name)    
            .HasMaxLength(100)
            .IsRequired();
    }
}

This is my Program.cs file:

var builder = WebApplication.CreateBuilder(args);

builder.Services.TryAddSingleton(TimeProvider.System);

builder.Services
       .AddScoped<IProductRepository, ProductRepository>()
       .AddScoped<ICategoryRepository, CategoryRepository>()
       .AddScoped<ProductsService>()
       .AddScoped<CategoryService>()
       .AddScoped<LibraryContext>()
       .AddScoped<IShopUnitOfWork, ShopUnitOfWork>()
       .AddScoped<IUnitOfWork, UnitOfWork>()
       .AddControllers(options => options.SuppressAsyncSuffixInActionNames = false)

       .AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);

// https://gavilan.blog/2021/05/19/fixing-the-error-a-possible-object-cycle-was-detected-in-different-versions-of-asp-net-core/

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddDbContext<LibraryContext>(options =>
    {
        options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));
    });

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    {
        options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));
    });

builder.Services.AddIdentityApiEndpoints<ShopUser>().AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddSwaggerGen();

var app = builder.Build();

// configure request pipeline
app.MapControllers();

app.UseCors (x => x
        .AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader() );

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapIdentityApi<ShopUser>();

app.Run();

I have already run an initial migration and database update before I added ASP.NET Core Identity to my project.

When I try to run a migration just for the Core Identity, it just stalls and doesn't finish.

dotnet ef migrations add CreateIdentitySchema -Context ApplicationDbContext

It never completes and gets to the step "Build Started"

I am new to Entity Framework Core and ASP.NET Core Identity. I suspect that it is the previous migration that is causing the stall.

I am following a .NET 8 Identity Core tutorial: https://servicestack.net/posts/identity-migration

But in the tutorial they only have the ASP.NET Core Identity integrated with their app and they have no other previous migrations.

Can anyone enlighten me as to how to proceed?

-UPDATE- I reran the above migration but using -v for verbose and now I can see where it gets stuck. Hopefully this will give someone some insight as to how to solve my issue. The below logging from the -v is where it gets stuck and doesn't move any further.

PM> dotnet ef database update -Context ApplicationDbContext -v
Using project 'C:\Users\david\Documents\Programming\Projects\dotnet\ShopBack\src\ShopBack\ShopBack.csproj'.
Using startup project 'C:\Users\david\Documents\Programming\Projects\dotnet\ShopBack\src\ShopBack\ShopBack.csproj'.
Writing 'C:\Users\david\Documents\Programming\Projects\dotnet\ShopBack\src\ShopBack\obj\ShopBack.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\david\AppData\Local\Temp\tmps53upb.tmp /verbosity:quiet /nologo C:\Users\david\Documents\Programming\Projects\dotnet\ShopBack\src\ShopBack\ShopBack.csproj
Writing 'C:\Users\david\Documents\Programming\Projects\dotnet\ShopBack\src\ShopBack\obj\ShopBack.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\david\AppData\Local\Temp\tmpn53xaz.tmp /verbosity:quiet /nologo C:\Users\david\Documents\Programming\Projects\dotnet\ShopBack\src\ShopBack\ShopBack.csproj

Solution

  • I was pretty sure I had tried everything but this one person on Youtube was giving a tutorial on how to change the Id on IdentityUser to be an int rather than a Guid and he ran these commands instead to create the Migration files and then update the database:

    PM> add-migration addIdentityTable -c ApplicationDbContext
    Then
    PM> update-database -Context ApplicationDbContext
    

    and that worked. My migration .cs files were created and after running the update database my database schema was generated and I can create users, log them in, etc.