I need to create navigation property to AspNetUser table (Microsoft.AspNetCore.Identity.EntityFrameworkCore), but my table is defined in different context. Both contexts point to same database (but have different schema).
I already found a way to do this when i have full control over configuration (example below), but because AspNetUser is configured internally by IdentityDbContext i cannot use it.
I could probably decompile IdentityDbContext and extract table configurations so i can use them in by second context, but that feels wrong (and may break when nuget updates) ...
Any ideas?
Ps: Reason why second db context is that i would like to have ability not registering second context at all
My current example of configuration
CurrencyDbContext
public sealed class Currency
{
internal const string TableName = "Currencies";
public int Id { get; set; }
}
internal sealed class CurrencyConfiguration : IEntityTypeConfiguration<Currency>
{
private readonly bool _IsExternal;
public CurrencyConfiguration(bool isExternal) {
_IsExternal = isExternal;
}
public void Configure(EntityTypeBuilder<Currency> builder) {
var table = builder.ToTable(Currency.TableName, CurrencyDbContext.Schema);
if (_IsExternal) {
table.Metadata.SetIsTableExcludedFromMigrations(true);
}
builder.HasKey(o => o.Id);
}
}
internal sealed class CurrencyDbContext : DbContext
{
public const string Schema = "core";
public CurrencyDbContext(DbContextOptions<CurrencyDbContext> options) : base(options) {
}
public DbSet<Currency> Currencies { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder builder) {
builder.ApplyConfiguration(new CurrencyConfiguration(false));
}
}
ItemDbContext
public sealed class Item
{
internal const string TableName = "Items";
[Obsolete("Do not use this constructor. For EF Core use only.", true)]
internal Currency() : this(null!) {
}
public Currency(Currency currency) {
Currency = currency;
}
public int Id { get; set; }
public Currency Currency { get; set; }
}
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);
}
}
internal sealed class ItemDbContext : DbContext
{
public const string Schema = "item";
public ItemDbContext(DbContextOptions<ItemDbContext> options) : base(options) {
}
public DbSet<Item> Items { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder builder) {
builder.ApplyConfiguration(new ItemConfiguration(false));
builder.ApplyConfiguration(new CurrencyConfiguration(true));
}
}
Register a cut down "User" entity into your ItemDbContext. The IdentityDbContext should only be used for authentication. While a DbContext cannot declare two entities pointing at the same table, there is nothing wrong with having the same table referenced by entities in two different DbContexts, you can even use the same entity class definition. This is fairly common when it comes to using bounded DbContexts where many entities might be shared between business functionality boundaries served by specific DbContext instances.
Though when it comes to User I would recommend using a cut-down version with just the Id and fields you care to display/use like name and Email address. This way your ItemDbContext entities can maintain their navigation properties back to a User record in the DB without potentially exposing authentication secrets to tampering intentionally or accidentally in the normal course of associating records to users. All auth checks etc. go through the IdentityDbContext.