如何跨不同DbContext正确创建指向AspNetUser表的导航属性?
解决方案:跨DbContext(不同Schema)关联Identity的AspNetUser表
针对你遇到的问题——在独立DbContext中关联由IdentityDbContext管理的AspNetUser表(不同Schema、同一数据库),且不想依赖反编译或注册额外Context,可按以下步骤实现:
1. 定义AspNetUser的映射实体
在目标DbContext(如你的ItemDbContext)所在项目中,创建与AspNetUser表结构匹配的实体类(只需包含你需要的字段,主键必须与Identity一致):
public sealed class AppUser { // 对应AspNetUsers表的主键,需与Identity配置一致 public string Id { get; set; } = null!; // 其他需要的字段,如UserName等 public string UserName { get; set; } = null!; }
2. 配置外部实体映射
创建该实体的配置类,指定对应的表名、Schema,并标记为排除迁移(因为表由IdentityDbContext管理):
internal sealed class AppUserConfiguration : IEntityTypeConfiguration<AppUser> { public void Configure(EntityTypeBuilder<AppUser> builder) { // 映射到Identity的AspNetUsers表,替换为你的Identity Schema(比如"auth") var table = builder.ToTable("AspNetUsers", "auth"); // 标记为外部表,不参与当前Context的迁移 table.Metadata.SetIsTableExcludedFromMigrations(true); // 配置主键,与Identity保持一致 builder.HasKey(u => u.Id); // 若需要其他字段约束,可按需添加,比如索引等 builder.HasIndex(u => u.UserName).IsUnique(); } }
3. 修改目标实体与配置
在Item实体中添加导航属性:
public sealed class Item { internal const string TableName = "Items"; [Obsolete("Do not use this constructor. For EF Core use only.", true)] internal Item() : this(null!, null!) { } public Item(Currency currency, AppUser creator) { Currency = currency; Creator = creator; } public int Id { get; set; } public Currency Currency { get; set; } = null!; // 新增关联AspNetUser的导航属性 public AppUser Creator { get; set; } = null!; // 若需要外键字段,可显式声明(可选,EF Core可隐式生成) public string CreatorId { get; set; } = null!; }
更新ItemConfiguration,配置与AppUser的关联关系:
internal sealed class ItemConfiguration : IEntityTypeConfiguration<Item> { private readonly bool _IsExternal; public ItemConfiguration(bool isExternal) { _IsExternal = isExternal; } public void Configure(EntityTypeBuilder<Item> builder) { var table = builder.ToTable(Item.TableName, ItemDbContext.Schema); if (_IsExternal) { table.Metadata.SetIsTableExcludedFromMigrations(true); } builder.HasKey(o => o.Id); builder.Property(o => o.Id).UseIdentityColumn(); builder.HasOne(b => b.Currency).WithMany().OnDelete(DeleteBehavior.Restrict); // 配置与AppUser的关联 builder.HasOne(i => i.Creator) .WithMany() // 若Identity侧不需要反向导航,用WithMany() .HasForeignKey(i => i.CreatorId) // 对应显式外键字段,若无则EF会生成隐式外键 .OnDelete(DeleteBehavior.Restrict); } }
4. 在目标DbContext中应用配置
修改ItemDbContext的OnModelCreating方法,添加AppUserConfiguration的配置:
internal sealed class ItemDbContext : DbContext { public const string Schema = "item"; public ItemDbContext(DbContextOptions<ItemDbContext> options) : base(options) { } public DbSet<Item> Items { get; set; } = null!; // 无需注册DbSet<AppUser>,除非你需要直接查询,若不需要可省略 // public DbSet<AppUser> Users { get; set; } = null!; protected override void OnModelCreating(ModelBuilder builder) { builder.ApplyConfiguration(new ItemConfiguration(false)); builder.ApplyConfiguration(new CurrencyConfiguration(true)); // 应用AppUser的外部表配置 builder.ApplyConfiguration(new AppUserConfiguration()); } }
关键说明
- 无需注册
ItemDbContext:若只是用于查询或代码优先映射,可直接通过DbContextOptions手动实例化,无需依赖DI注册 - 兼容性:这种方式不依赖Identity内部配置,即使NuGet包更新,只要
AspNetUsers表的核心结构(主键、关联字段)不变,就不会受影响 - 字段匹配:确保
AppUser实体的字段与数据库中AspNetUsers表的字段名称、类型完全一致,避免映射错误
内容的提问来源于stack exchange,提问作者CzBuCHi
相关产品推荐
相关产品推荐

