abpframework / abp

Open-source web application framework for ASP.NET Core! Offers an opinionated architecture to build enterprise software solutions with best practices on top of the .NET. Provides the fundamental infrastructure, cross-cutting-concern implementations, startup templates, application modules, UI themes, tooling and documentation.
https://abp.io
GNU Lesser General Public License v3.0
12.92k stars 3.44k forks source link

The exception 'The entity type 'ExtraPropertyDictionary' requires a primary key to be defined #20793

Open Xiaolefu93 opened 1 month ago

Xiaolefu93 commented 1 month ago

Is there an existing issue for this?

Description

hi,when I try "dotnet ef migrations",get the error "The exception 'The entity type 'ExtraPropertyDictionary' requires a primary key to be defined". And then I try add [key]attribute in the entity or add p.HasKey(x => x.Id) in OnModelCreating,but get the same error "The exception 'The entity type 'ExtraPropertyDictionary' requires a primary key to be defined"

namespace BoilerPlateLayer.EntityFrameworkCore;

[ReplaceDbContext(typeof(IIdentityDbContext))] [ReplaceDbContext(typeof(ITenantManagementDbContext))] [ConnectionStringName("Default")] public class BoilerPlateLayerDbContext : AbpDbContext, IIdentityDbContext, ITenantManagementDbContext { / Add DbSet properties for your Aggregate Roots / Entities here. /

#region Entities from the modules

/* Notice: We only implemented IIdentityDbContext and ITenantManagementDbContext
 * and replaced them for this DbContext. This allows you to perform JOIN
 * queries for the entities of these modules over the repositories easily. You
 * typically don't need that for other modules. But, if you need, you can
 * implement the DbContext interface of the needed module and use ReplaceDbContext
 * attribute just like IIdentityDbContext and ITenantManagementDbContext.
 *
 * More info: Replacing a DbContext of a module ensures that the related module
 * uses this DbContext on runtime. Otherwise, it will use its own DbContext class.
 */

//Identity
public DbSet<IdentityUser> Users { get; set; }
public DbSet<IdentityRole> Roles { get; set; }
public DbSet<IdentityClaimType> ClaimTypes { get; set; }
public DbSet<OrganizationUnit> OrganizationUnits { get; set; }
public DbSet<IdentitySecurityLog> SecurityLogs { get; set; }
public DbSet<IdentityLinkUser> LinkUsers { get; set; }
public DbSet<IdentityUserDelegation> UserDelegations { get; set; }
public DbSet<IdentitySession> Sessions { get; set; }
// Tenant Management
public DbSet<Tenant> Tenants { get; set; }
public DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }

#endregion

DbSet<Product> Products;
DbSet<Category> Categories;

public BoilerPlateLayerDbContext(DbContextOptions<BoilerPlateLayerDbContext> options)
    : base(options)
{

}

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

    /* Include modules to your migration db context */

    builder.ConfigurePermissionManagement();
    builder.ConfigureSettingManagement();
    builder.ConfigureBackgroundJobs();
    builder.ConfigureAuditLogging();
    builder.ConfigureIdentity();
    builder.ConfigureOpenIddict();
    builder.ConfigureFeatureManagement();
    builder.ConfigureTenantManagement();

    /* Configure your own tables/entities inside here */

    //builder.Entity<YourEntity>(b =>
    //{
    //    b.ToTable(BoilerPlateLayerConsts.DbTablePrefix + "YourEntities", BoilerPlateLayerConsts.DbSchema);
    //    b.ConfigureByConvention(); //auto configure for the base class props
    //    //...
    //});

    builder.Entity<Category>(c =>
    {
        c.ToTable("Categories");
        c.Property(x => x.Name).HasMaxLength(CategoryConst.MaxNameLength).IsRequired();
        c.HasIndex(x => x.Name);
    });

    builder.Entity<Product>(p =>
    {
        p.ToTable("Products");
        p.Property(x => x.Name).HasMaxLength(ProductConst.MaxNameLength).IsRequired();
        p.HasIndex(x => x.Name).IsUnique();
        p.HasOne(x => x.Category).WithMany().HasForeignKey(x => x.CategoryId).OnDelete(DeleteBehavior.Restrict).IsRequired();
    });
}

}

Reproduction Steps

No response

Expected behavior

No response

Actual behavior

No response

Regression?

No response

Known Workarounds

No response

Version

8.3.0

User Interface

Common (Default)

Database Provider

EF Core (Default)

Tiered or separate authentication server

None (Default)

Operation System

Windows (Default)

Other information

No response

Xiaolefu93 commented 1 month ago

the Product and Category entity:

public class Category :AuditedAggregateRoot<Guid>
{
    public string Name { get; set; }
}

public class Product : FullAuditedAggregateRoot<Guid>
{
    public Category Category { get; set; }

    public Guid CategoryId { get; set; }

    public string Name { get; set; }

    public float Price { get; set; }

    public bool IsFreeCargo { get; set; }

    public DateTime ReleaseTime { get; set; }

    public ProductStockState StockState { get; set; }
}
maliming commented 1 month ago

Add ConfigureByConvention();

builder.Entity<Category>(c =>
{
    c.ConfigureByConvention();
    c.ToTable("Categories");
    c.Property(x => x.Name).HasMaxLength(CategoryConst.MaxNameLength).IsRequired();
    c.HasIndex(x => x.Name);
});

builder.Entity<Product>(p =>
{
    p.ConfigureByConvention();
    p.ToTable("Products");
    p.Property(x => x.Name).HasMaxLength(ProductConst.MaxNameLength).IsRequired();
    p.HasIndex(x => x.Name).IsUnique();
    p.HasOne(x => x.Category).WithMany().HasForeignKey(x => x.CategoryId).OnDelete(DeleteBehavior.Restrict).IsRequired();
});
Xiaolefu93 commented 1 month ago

thank you,and then I want to ask "why add ConfigureByConvention"