Reference one Table to "one of many" Table

46 Views Asked by At

I have a Table called Command

public class Command
{
    public int Id { get; set; }      
    public CommandParameterBaseEntity Parameter { get; set; } = null!;
}

and two (or more) other tables with the same basetype which only one of them can refer to Command.

The BaseClass oft the other Tables:

public abstract class CommandParameterBaseEntity 
{
    public int Id { get; set; }
    public int CommandId { get; set; }
    public Command Command { get; set; } = null!;
}

the derived tables:

public class ParameterA : CommandParameterBaseEntity 
{
    public int SomeValue { get; set; }
}

public class ParameterB : CommandParameterBaseEntity 
{
    public string AnotherValue { get; set; } = string.Empty;
}

The relationships:

public class CommandDbCfg 
{
    public override void Configure(EntityTypeBuilder<Command> builder)
    {
        builder.ToTable("Command");
        builder.HasKey(k => k.Id);
    }
}

public abstract class CommandParameterBaseEntityDbCfg<T> : IEntityTypeConfiguration<T> where T : CommandParameterBaseEntity 
{
    public virtual void Configure(EntityTypeBuilder<T> builder)
    {
        builder.HasKey(k => k.Id);

        builder.HasOne(p => p.Command)
            .WithOne(s => s.Parameter as T)
            .HasForeignKey<CommandParameterBaseEntity>(p => p.CommandId)
            .OnDelete(DeleteBehavior.Cascade);
    }
}

public class ParameterADbCfg : CommandParameterBaseEntityDbCfg<ParameterA>
{
    public override void Configure(EntityTypeBuilder<ParameterA> builder)
    {
        builder.ToTable("ParameterA");
        base.Configure(builder);
    }
}

public class ParameterBDbCfg : CommandParameterBaseEntityDbCfg<ParameterB>
{
    public override void Configure(EntityTypeBuilder<ParameterB> builder)
    {
        builder.ToTable("ParameterB");
        base.Configure(builder);
    }
}

With this configuration I get the error:

A key cannot be configured on 'ParameterA' because it is a derived type. The key must be configured on the root type 'CommandParameterBaseEntity'. If you did not intend for 'CommandParameterBaseEntity' to be included in the model, ensure that it is not referenced by a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation on a type that is included in the model.

Is it possible to create this kind of realtionship in efc without having a navigation model for every parameter in the command model?

1

There are 1 best solutions below

0
Fabio Caruso On BEST ANSWER

I modified your code, so compiles without errors, creating 4 tables:

public class CommandDbCfg : IEntityTypeConfiguration<Command>
{
    public void Configure(EntityTypeBuilder<Command> builder)
    {
        builder.ToTable("Command");
        builder.HasKey(k => k.Id);
    }
}

public class CommandParameterBaseEntityDbCfg<T> : IEntityTypeConfiguration<T> where T : CommandParameterBaseEntity
{
    public virtual void Configure(EntityTypeBuilder<T> builder)
    {
        builder.HasKey(k => k.Id);
        builder.HasOne(p => p.Command)
            .WithOne(s => s.Parameter as T)
            .HasForeignKey<CommandParameterBaseEntity>(p => p.CommandId)
            .OnDelete(DeleteBehavior.Cascade);
    }
}

public class ParameterADbCfg : IEntityTypeConfiguration<ParameterA>
{
    public void Configure(EntityTypeBuilder<ParameterA> builder)
    {
        builder.ToTable("ParameterA");
    }
}

public class ParameterBDbCfg : IEntityTypeConfiguration<ParameterB>
{
    public void Configure(EntityTypeBuilder<ParameterB> builder)
    {
        builder.ToTable("ParameterB");
    }
}

...and into your DbContext class:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{                
    base.OnModelCreating(modelBuilder);
    new CommandDbCfg().Configure(modelBuilder.Entity<Command>());
    new CommandParameterBaseEntityDbCfg<CommandParameterBaseEntity().Configure(modelBuilder.Entity<CommandParameterBaseEntity>());
    new ParameterADbCfg().Configure(modelBuilder.Entity<ParameterA>());
    new ParameterBDbCfg().Configure(modelBuilder.Entity<ParameterB>());        
}