Freigeben über


Vererbung

EF kann eine .NET-Typ-Hierarchie auf eine Datenbank abbilden. Auf diese Weise können Sie Ihre .NET-Entitäten wie gewohnt in Code schreiben, basis- und abgeleitete Typen verwenden und EF nahtlos das entsprechende Datenbankschema erstellen, Abfragen ausstellen usw. Die tatsächlichen Details dazu, wie eine Typhierarchie zugeordnet wird, sind vom Anbieter abhängig; Diese Seite beschreibt die Vererbungsunterstützung im Kontext einer relationalen Datenbank.

Abbildung der Entitätstyphierarchie

Standardmäßig sucht EF nicht automatisch nach Basis- oder abgeleiteten Typen. Dies bedeutet, dass Sie explizit diesen Typ in Ihrem Modell angeben müssen, wenn ein CLR-Typ in Ihrer Hierarchie zugeordnet werden soll. Wenn Sie z. B. nur den Basistyp einer Hierarchie angeben, wird EF Core nicht dazu führen, dass alle untergeordneten Typen implizit eingeschlossen werden.

Im folgenden Beispiel wird ein DbSet für Blog und dessen Unterklasse RssBlog verfügbar gemacht. Wenn Blog eine andere Unterklasse vorhanden ist, wird sie nicht im Modell enthalten sein.

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<RssBlog> RssBlogs { get; set; }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

public class RssBlog : Blog
{
    public string RssUrl { get; set; }
}

Hinweis

Datenbankspalten lassen bei Verwendung der TPH-Zuordnung automatisch Nullwerte zu. Ein Beispiel: Die RssUrl Spalte kann NULL-Werte aufweisen, da normale Blog Instanzen diese Eigenschaft nicht besitzen.

Wenn Sie DbSet für eine oder mehrere Entitäten in der Hierarchie nicht verfügbar machen möchten, können Sie auch die Fluent-API verwenden, um sicherzustellen, dass sie im Modell enthalten sind.

Tipp

Wenn Sie nicht auf Konventionen angewiesen sind, können Sie den Basistyp explizit mit der Verwendung HasBaseTypeangeben. Sie können auch einen .HasBaseType((Type)null) Entitätstyp aus der Hierarchie entfernen.

Konfiguration von Tabellen pro Hierarchie und Diskriminator

Standardmäßig ordnet EF die Vererbung mithilfe des Muster Tabelle pro Hierarchie (TPH) zu. TPH verwendet eine einzelne Tabelle, um die Daten für alle Typen in der Hierarchie zu speichern, und eine Diskriminatorspalte wird verwendet, um zu identifizieren, welchen Typ jede Zeile darstellt.

Das obige Modell wird dem folgenden Datenbankschema zugeordnet (beachten Sie die implizit erstellte Discriminator Spalte, die angibt, welcher Typ Blog in jeder Zeile gespeichert ist).

Screenshot: Ergebnisse der Abfrage der Hierarchie der Blogentität mithilfe des Musters „Tabelle pro Hierarchie”

Sie können den Namen und Typ der Diskriminatorspalte und die Werte konfigurieren, die zum Identifizieren der einzelnen Typen in der Hierarchie verwendet werden:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasDiscriminator<string>("blog_type")
        .HasValue<Blog>("blog_base")
        .HasValue<RssBlog>("blog_rss");
}

In den obigen Beispielen hat EF den Diskriminator implizit als Schatteneigenschaft zur Basisentität der Hierarchie hinzugefügt. Diese Eigenschaft kann wie jede andere konfiguriert werden:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property("blog_type")
        .HasMaxLength(200);
}

Schließlich kann der Diskriminator auch einer regulären .NET-Eigenschaft in Ihrer Entität zugeordnet werden:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasDiscriminator(b => b.BlogType);

    modelBuilder.Entity<Blog>()
        .Property(e => e.BlogType)
        .HasMaxLength(200)
        .HasColumnName("blog_type");
        
    modelBuilder.Entity<RssBlog>();
}

Beim Abfragen von abgeleiteten Entitäten, die das TPH-Muster verwenden, fügt EF Core in der Abfrage ein Prädikat über die Diskriminatorspalte hinzu. Dieser Filter stellt sicher, dass keine zusätzlichen Zeilen für Basistypen oder gleichgeordnete Typen abgerufen werden, die nicht im Ergebnis enthalten sind. Dieses Filterprädikat wird für den Basisentitätstyp übersprungen, da die Abfrage der Basisentität Ergebnisse für alle Entitäten in der Hierarchie abruft. Wenn ergebnisse aus einer Abfrage materialisiert werden, wenn wir über einen Diskriminatorwert stoßen, der keinem Entitätstyp im Modell zugeordnet ist, lösen wir eine Ausnahme aus, da wir nicht wissen, wie die Ergebnisse materialisiert werden. Dieser Fehler tritt nur auf, wenn Ihre Datenbank Zeilen mit Diskriminatorwerten enthält, die nicht im EF-Modell zugeordnet sind. Wenn Sie über solche Daten verfügen, können Sie die Diskriminatorzuordnung im EF Core-Modell als unvollständig markieren, um anzugeben, dass wir immer Filterprädikat für die Abfrage eines beliebigen Typs in der Hierarchie hinzufügen sollten. Der IsComplete(false)-Aufruf der Diskriminatorkonfiguration kennzeichnet, dass die Zuordnung unvollständig ist.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasDiscriminator()
        .IsComplete(false);
}

Freigegebene Spalten

Wenn zwei gleichgeordnete Entitätstypen in der Hierarchie standardmäßig über eine Eigenschaft mit demselben Namen verfügen, werden sie zwei separaten Spalten zugeordnet. Wenn ihr Typ jedoch identisch ist, können sie derselben Datenbankspalte zugeordnet werden:

public class MyContext : DbContext
{
    public DbSet<BlogBase> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .HasColumnName("Url");

        modelBuilder.Entity<RssBlog>()
            .Property(b => b.Url)
            .HasColumnName("Url");
    }
}

public abstract class BlogBase
{
    public int BlogId { get; set; }
}

public class Blog : BlogBase
{
    public string Url { get; set; }
}

public class RssBlog : BlogBase
{
    public string Url { get; set; }
}

Hinweis

Relationale Datenbankanbieter, z. B. SQL Server, verwenden das Diskriminator-Prädikat beim Abfragen freigegebener Spalten bei Verwendung einer Umwandlung nicht automatisch. Die Abfrage Url = (blog as RssBlog).Url würde auch den Url Wert für die gleichgeordneten Blog Zeilen zurückgeben. Um die Abfrage auf RssBlog Entitäten einzuschränken, müssen Sie manuell einen Filter für den Diskriminator hinzufügen, wie zum Beispiel Url = blog is RssBlog ? (blog as RssBlog).Url : null.

Tabelle-pro-Typ-Konfiguration

Im TPT-Zuordnungsmuster werden alle Typen einzelnen Tabellen zugeordnet. Eigenschaften, die ausschließlich zu einem Basistyp oder abgeleitetem Typ gehören, werden in einer Tabelle gespeichert, die diesem Typ zugeordnet ist. Tabellen, die abgeleiteten Typen zugeordnet sind, speichern auch einen Fremdschlüssel, der die abgeleitete Tabelle mit der Basistabelle verknüpft.

modelBuilder.Entity<Blog>().ToTable("Blogs");
modelBuilder.Entity<RssBlog>().ToTable("RssBlogs");

Tipp

Statt jeden Entitätstyp aufzurufen ToTable , können Sie jeden Stammentitätstyp aufrufen modelBuilder.Entity<Blog>().UseTptMappingStrategy() , und die Tabellennamen werden von EF generiert.

Tipp

Informationen zum Konfigurieren verschiedener Spaltennamen für die Primärschlüsselspalten in jeder Tabelle finden Sie unter "Tabellenspezifische Facetkonfiguration".

EF erstellt das folgende Datenbankschema für das oben genannte Modell.

CREATE TABLE [Blogs] (
    [BlogId] int NOT NULL IDENTITY,
    [Url] nvarchar(max) NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY ([BlogId])
);

CREATE TABLE [RssBlogs] (
    [BlogId] int NOT NULL,
    [RssUrl] nvarchar(max) NULL,
    CONSTRAINT [PK_RssBlogs] PRIMARY KEY ([BlogId]),
    CONSTRAINT [FK_RssBlogs_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([BlogId]) ON DELETE NO ACTION
);

Hinweis

Wenn die Primärschlüsseleinschränkung umbenannt wird, wird der neue Name auf alle Tabellen angewendet, die der Hierarchie zugeordnet sind, können zukünftige EF-Versionen die Einschränkung nur für eine bestimmte Tabelle umbenennen, wenn das Problem 19970 behoben ist.

Wenn Sie die Massenkonfiguration verwenden, können Sie den Spaltennamen für eine bestimmte Tabelle durch das Aufrufen von GetColumnName(IProperty, StoreObjectIdentifier) abrufen.

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    var tableIdentifier = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table);

    Console.WriteLine($"{entityType.DisplayName()}\t\t{tableIdentifier}");
    Console.WriteLine(" Property\tColumn");

    foreach (var property in entityType.GetProperties())
    {
        var columnName = property.GetColumnName(tableIdentifier.Value);
        Console.WriteLine($" {property.Name,-10}\t{columnName}");
    }

    Console.WriteLine();
}

Warnung

In vielen Fällen zeigt TPT eine schlechtere Leistung im Vergleich zu TPH. Weitere Informationen finden Sie in den Leistungsdokumenten.

Vorsicht

Spalten für einen abgeleiteten Typ werden verschiedenen Tabellen zugeordnet, daher können zusammengesetzte FK-Einschränkungen und Indizes, die sowohl die geerbten als auch deklarierten Eigenschaften verwenden, nicht in der Datenbank erstellt werden.

Tabelle-pro-konkreter-Typ-Konfiguration (TPC)

Im TPC-Zuordnungsmuster werden alle Typen einzelnen Tabellen zugeordnet. Jede Tabelle enthält Spalten für alle Eigenschaften des entsprechenden Entitätstyps. Dies behebt einige häufige Leistungsprobleme mit der TPT-Strategie.

Tipp

Das EF-Team präsentierte und sprach ausführlich über das TPC-Mapping in einer Folge des .NET Data Community Standup. Wie bei allen Community-Standup-Episoden können Sie die TPC-Episode jetzt auf YouTube ansehen.

modelBuilder.Entity<Blog>().UseTpcMappingStrategy()
    .ToTable("Blogs");
modelBuilder.Entity<RssBlog>()
    .ToTable("RssBlogs");

Tipp

Statt ToTable für jeden Entitätstyp aufzurufen, erzeugt modelBuilder.Entity<Blog>().UseTpcMappingStrategy() bei jedem Stammentitätstyp die Tabellennamen nach Konvention.

Tipp

Informationen zum Konfigurieren verschiedener Spaltennamen für die Primärschlüsselspalten in jeder Tabelle finden Sie unter "Tabellenspezifische Facetkonfiguration".

EF erstellt das folgende Datenbankschema für das oben genannte Modell.

CREATE TABLE [Blogs] (
    [BlogId] int NOT NULL DEFAULT (NEXT VALUE FOR [BlogSequence]),
    [Url] nvarchar(max) NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY ([BlogId])
);

CREATE TABLE [RssBlogs] (
    [BlogId] int NOT NULL DEFAULT (NEXT VALUE FOR [BlogSequence]),
    [Url] nvarchar(max) NULL,
    [RssUrl] nvarchar(max) NULL,
    CONSTRAINT [PK_RssBlogs] PRIMARY KEY ([BlogId])
);

TPC-Datenbankschema

Die TPC-Strategie ähnelt der TPT-Strategie, mit der Ausnahme, dass für jeden konkreten Typ in der Hierarchie eine andere Tabelle erstellt wird, aber Tabellen werden nicht für abstrakte Typen erstellt - daher der Name "Table-per-concrete-type". Wie bei TPT gibt die Tabelle selbst den Typ des gespeicherten Objekts an. Im Gegensatz zur TPT-Zuordnung enthält jede Tabelle jedoch Spalten für jede Eigenschaft im konkreten Typ und ihre Basistypen. TPC-Datenbankschemas werden denormalisiert.

Sehen Sie sich beispielsweise die Zuordnung dieses Hierarchie an:

public abstract class Animal
{
    protected Animal(string name)
    {
        Name = name;
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public abstract string Species { get; }

    public Food? Food { get; set; }
}

public abstract class Pet : Animal
{
    protected Pet(string name)
        : base(name)
    {
    }

    public string? Vet { get; set; }

    public ICollection<Human> Humans { get; } = new List<Human>();
}

public class FarmAnimal : Animal
{
    public FarmAnimal(string name, string species)
        : base(name)
    {
        Species = species;
    }

    public override string Species { get; }

    [Precision(18, 2)]
    public decimal Value { get; set; }

    public override string ToString()
        => $"Farm animal '{Name}' ({Species}/{Id}) worth {Value:C} eats {Food?.ToString() ?? "<Unknown>"}";
}

public class Cat : Pet
{
    public Cat(string name, string educationLevel)
        : base(name)
    {
        EducationLevel = educationLevel;
    }

    public string EducationLevel { get; set; }
    public override string Species => "Felis catus";

    public override string ToString()
        => $"Cat '{Name}' ({Species}/{Id}) with education '{EducationLevel}' eats {Food?.ToString() ?? "<Unknown>"}";
}

public class Dog : Pet
{
    public Dog(string name, string favoriteToy)
        : base(name)
    {
        FavoriteToy = favoriteToy;
    }

    public string FavoriteToy { get; set; }
    public override string Species => "Canis familiaris";

    public override string ToString()
        => $"Dog '{Name}' ({Species}/{Id}) with favorite toy '{FavoriteToy}' eats {Food?.ToString() ?? "<Unknown>"}";
}

public class Human : Animal
{
    public Human(string name)
        : base(name)
    {
    }

    public override string Species => "Homo sapiens";

    public Animal? FavoriteAnimal { get; set; }
    public ICollection<Pet> Pets { get; } = new List<Pet>();

    public override string ToString()
        => $"Human '{Name}' ({Species}/{Id}) with favorite animal '{FavoriteAnimal?.Name ?? "<Unknown>"}'" +
           $" eats {Food?.ToString() ?? "<Unknown>"}";
}

Bei Verwendung von SQL Server sind die für diese Hierarchie erstellten Tabellen:

CREATE TABLE [Cats] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
    [Name] nvarchar(max) NOT NULL,
    [FoodId] uniqueidentifier NULL,
    [Vet] nvarchar(max) NULL,
    [EducationLevel] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]));

CREATE TABLE [Dogs] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
    [Name] nvarchar(max) NOT NULL,
    [FoodId] uniqueidentifier NULL,
    [Vet] nvarchar(max) NULL,
    [FavoriteToy] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]));

CREATE TABLE [FarmAnimals] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
    [Name] nvarchar(max) NOT NULL,
    [FoodId] uniqueidentifier NULL,
    [Value] decimal(18,2) NOT NULL,
    [Species] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_FarmAnimals] PRIMARY KEY ([Id]));

CREATE TABLE [Humans] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
    [Name] nvarchar(max) NOT NULL,
    [FoodId] uniqueidentifier NULL,
    [FavoriteAnimalId] int NULL,
    CONSTRAINT [PK_Humans] PRIMARY KEY ([Id]));

Beachten Sie Folgendes:

  • Es gibt keine Tabellen für die Animal oder Pet Typen, da sich diese im Objektmodell befinden abstract . Denken Sie daran, dass C# keine Instanzen abstrakter Typen zulässt, und es gibt daher keine Situation, in der eine abstrakte Typinstanz in der Datenbank gespeichert wird.

  • Die Zuordnung von Eigenschaften in Basistypen wird für jeden konkreten Typ wiederholt. Beispielsweise verfügt jede Tabelle über eine Name Spalte, und sowohl Katzen als auch Hunde haben eine Vet Spalte.

  • Das Speichern einiger Daten in dieser Datenbank führt zu folgenden Ergebnissen:

Katzentisch

Id Name FoodId Tierarzt Bildungsniveau
1 Alina 99ca3e98-b26d-4a0c-d4ae-08da7aca624f Pengelly MBA
2 Mac 99ca3e98-b26d-4a0c-d4ae-08da7aca624f Pengelly Vorschule
8 Baxter 5dc5019e-6f72-454b-d4b0-08da7aca624f Bothell Pet Hospital Bsc

Tabelle für Hunde

Id Name FoodId Tierarzt FavoriteToy
3 Popup 011aaf6f-d588-4fad-d4ac-08da7aca624f Pengelly Herr Squirrel

FarmAnimals-Tabelle

Id Name FoodId Wert Arten
4 Clyde 1d495075-f527-4498-d4af-08da7aca624f 100,00 Equus africanus asinus

Menschentabelle

Id Name FoodId LieblingstierId
5 Wendy 5418fd81-7660-432f-d4b1-08da7aca624f 2
6 Arthur 59b495d4-0414-46bf-d4ad-08da7aca624f 1
9 Katie NULL 8

Beachten Sie, dass im Gegensatz zur TPT-Zuordnung alle Informationen für ein einzelnes Objekt in einer einzelnen Tabelle enthalten sind. Anders als bei der TPH-Zuordnung gibt es keine Kombination aus Spalte und Zeile in einer Tabelle, in der das Modell nie verwendet wird. Wir werden unten sehen, wie diese Merkmale für Abfragen und Speicher wichtig sein können.

Schlüsselgenerierung

Die gewählte Vererbungszuordnungsstrategie hat Konsequenzen für die Erstellung und Verwaltung von Primärschlüsselwerten. Schlüssel in TPH sind einfach, da jede Entitätsinstanz durch eine einzelne Zeile in einer einzelnen Tabelle dargestellt wird. Jede Art von Schlüsselwertgenerierung kann verwendet werden, und es sind keine zusätzlichen Einschränkungen erforderlich.

Für die TPT-Strategie gibt es immer eine Zeile in der Tabelle, die dem Basistyp der Hierarchie zugeordnet ist. Jede Art von Schlüsselgenerierung kann in dieser Zeile verwendet werden, und die Schlüssel für andere Tabellen werden mithilfe von Fremdschlüsseleinschränkungen mit dieser Tabelle verknüpft.

Die Dinge werden für TPC etwas komplizierter. Zunächst ist es wichtig zu verstehen, dass EF Core erfordert, dass alle Entitäten in einer Hierarchie einen eindeutigen Schlüsselwert aufweisen, auch wenn die Entitäten unterschiedliche Typen aufweisen. Beispielsweise kann ein Hund mit unserem Beispielmodell nicht den gleichen ID-Schlüsselwert wie eine Katze haben. Im Gegensatz zu TPT gibt es keine gemeinsame Tabelle, die als einzelne Stelle fungieren kann, an der sich Schlüsselwerte befinden und generiert werden können. Dies bedeutet, dass eine einfache Identity Spalte nicht verwendet werden kann.

Für Datenbanken, die Sequenzen unterstützen, können Schlüsselwerte mithilfe einer einzelnen Sequenz generiert werden, auf die in der Standardeinschränkung für jede Tabelle verwiesen wird. Dies ist die Strategie, die in den oben gezeigten TPC-Tabellen verwendet wird, wobei jede Tabelle folgendes hat:

[Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence])

AnimalSequence ist eine Datenbanksequenz, die von EF Core erstellt wird. Diese Strategie wird bei Verwendung des EF Core-Datenbankanbieters für SQL Server standardmäßig für TPC-Hierarchien verwendet. Datenbankanbieter für andere Datenbanken, die Sequenzen unterstützen, sollten einen ähnlichen Standardwert aufweisen. Andere Schlüsselgenerierungsstrategien, die Sequenzen wie Hi-Lo-Muster verwenden, können auch mit TPC verwendet werden.

Standardidentitätsspalten funktionieren zwar nicht mit TPC, aber es ist möglich, Identitätsspalten zu verwenden, wenn jede Tabelle mit einem geeigneten Ausgangswert konfiguriert und erhöht wird, sodass die für jede Tabelle generierten Werte niemals in Konflikt stehen. Beispiel:

modelBuilder.Entity<Cat>().ToTable("Cats", tb => tb.Property(e => e.Id).UseIdentityColumn(1, 4));
modelBuilder.Entity<Dog>().ToTable("Dogs", tb => tb.Property(e => e.Id).UseIdentityColumn(2, 4));
modelBuilder.Entity<FarmAnimal>().ToTable("FarmAnimals", tb => tb.Property(e => e.Id).UseIdentityColumn(3, 4));
modelBuilder.Entity<Human>().ToTable("Humans", tb => tb.Property(e => e.Id).UseIdentityColumn(4, 4));

Wichtig

Wenn Sie diese Strategie verwenden, ist es schwieriger, abgeleitete Typen später hinzuzufügen, da die Gesamtanzahl der Typen in der Hierarchie vorher bekannt sein muss.

SQLite unterstützt keine Sequenzen oder Identity Seed/Increment, und daher wird die Wertgenerierung ganzzahliger Schlüssel nicht unterstützt, wenn SQLite mit der TPC-Strategie verwendet wird. Clientseitige Generierung oder global eindeutige Schlüssel ( z. B. GUIDs ) werden jedoch für jede Datenbank unterstützt, einschließlich SQLite.

Fremdschlüsseleinschränkungen

Die TPC-Zuordnungsstrategie erstellt ein denormalisiertes SQL-Schema – dies ist ein Grund, warum einige Datenbank-Puristen dagegen sind. Betrachten Sie z. B. die Fremdschlüsselspalte FavoriteAnimalId. Der Wert in dieser Spalte muss mit dem Primärschlüsselwert einiger Tiere übereinstimmen. Dies kann in der Datenbank mit einer einfachen FK-Einschränkung erzwungen werden, wenn TPH oder TPT verwendet wird. Beispiel:

CONSTRAINT [FK_Animals_Animals_FavoriteAnimalId] FOREIGN KEY ([FavoriteAnimalId]) REFERENCES [Animals] ([Id])

Aber bei Verwendung von TPC wird der Primärschlüssel für jedes gegebene Tier in der Tabelle gespeichert, die dem konkreten Typ dieses Tieres entspricht. Beispielsweise wird der Primärschlüssel einer Katze in der Cats.Id Spalte gespeichert, während der Primärschlüssel eines Hundes in der Dogs.Id Spalte gespeichert ist usw. Dies bedeutet, dass für diese Beziehung keine FK-Einschränkung erstellt werden kann.

In der Praxis ist dies kein Problem, solange die Anwendung nicht versucht, ungültige Daten einzufügen. Wenn beispielsweise alle Daten von EF Core eingefügt werden und Navigationen zum Verknüpfen von Entitäten verwendet werden, wird sichergestellt, dass die FK-Spalte jederzeit gültige PK-Werte enthält.

Zusammenfassung und Anleitung

In der Zusammenfassung ist TPH in der Regel für die meisten Anwendungen in Ordnung und ist ein guter Standardwert für eine vielzahl von Szenarien. Fügen Sie daher nicht die Komplexität von TPC hinzu, wenn Sie sie nicht benötigen. Wenn Ihr Code hauptsächlich Abfragen für Entitäten vieler Typen ausführt, z. B. indem Sie Abfragen für den Basistyp schreiben, sollten Sie sich eher für TPH anstelle von TPC entscheiden.

Das heißt, TPC ist auch eine gute Zuordnungsstrategie, die verwendet werden kann, wenn Ihr Code hauptsächlich nach Entitäten eines einzelnen Blatttyps abfragt, und Ihre Benchmarks zeigen eine Verbesserung im Vergleich zu TPH.

Verwenden Sie TPT nur, wenn Sie durch externe Faktoren dazu gezwungen sind.