Ho dei modelli che ereditano gli uni dagli altri, ma sto facendo fatica a fare in modo che la corretta configurazione delle API si comporti come voglio. Diciamo che ho una classe base che definisce alcune proprietà core
public class Entity
{
public int Id { get; set; }
public string Title { get; set };
}
E una sottoclasse per il libro
public class Book : Entity
{
public int Edition { get; set; }
}
In questo modo posso avere libri, riviste, opuscoli, fumetti, discorsi ecc., Tutti ereditati dalla mia Entità, e non devo definire la relazione su ogni classe.
Adesso aggiungo i miei DbSet al DbContext come questo
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Book> Books { get; set; }
public virtual DbSet<Magazine> Magazines { get; set; }
public virtual DbSet<Comic> Comics { get; set; }
}
E infine aggiungo-migration Initial.
La mia migrazione ora crea tabelle separate (TPC) per ogni tipo. Perfezionare.
Il problema si presenta quando provo a configurare la mia classe base usando l'API fluente.
Aggiungo una configurazione per Entity
class EntityConfiguration : IEntityTypeConfiguration<Entity>
{
public void Configure(EntityTypeBuilder<Entity> builder)
{
builder.HasKey(e => e.Id);
builder.Property(e => e.Title).IsRequired();
}
}
L'idea è che ora ho solo bisogno di configurare l'entità di base, e tutte le tabelle per le sottoclassi dovrebbero prendere la configurazione.
Aggiungo la mia configurazione al metodo OnModelCreating di DbContext.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new EntityConfiguration());
}
Quando aggiungo-migrazione, finisco con questo
migrationBuilder.CreateTable(
name: "Entity",
columns: table => new
{
Edition = table.Column<int>(nullable: true),
Name = table.Column<string>(nullable: true),
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Discriminator = table.Column<string>(nullable: false),
Title = table.Column<string>(nullable: false),
Frequency = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Entity", x => x.Id);
});
Provando a configurare la classe base, EF sta ora scendendo lungo la rotta TPH e creando una singola tabella per Entity con una colonna discriminante.
c'è un modo per evitarlo? È anche possibile configurare la classe base e tutte le tabelle concrete prendono la configurazione sulla classe base ma creano tabelle per le sottoclassi?
NOTA: ho provato a configurare Entity direttamente nel metodo DbContext OnModelCreating, invece di utilizzare una classe di configurazione separata, ma questo si comporta ugualmente.
I documenti EF Core in realtà dicono che TPC non è supportato, il che è strano perché crea tabelle separate per le sottoclassi finché non provo a configurare la classe base.
Ho provato a usare Ignore () per sopprimere il TPH, ma questo non ha alcun effetto.
L'esempio dato non è reale. Il mio progetto attuale ha molte altre classi che hanno tutte proprietà e relazioni comuni, quindi voglio evitare di dover configurare le stesse cose più e più volte.
Hai ragione nel dire che EF Core
non supporta il TPC al momento della scrittura.
Tuttavia, sembra che ci sia un modo per aggirare questo problema (per generare almeno lo script 'Up').
Rimuovere le registrazioni FluentAPI
e utilizzare le Annotations
sulle proprietà della classe Entity:
public abstract class Entity
{
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
}
Inoltre, poiché TPC è la classe Table Per (Concrete), è buona pratica rendere la classe che si sta ereditando abstract
.