Teilen über


Vererbung

EF kann einer Datenbank eine .NET-Typhierarchie zuordnen. So können Sie Ihre .NET-Entitäten wie gewohnt im Code schreiben und grundlegende und abgeleitete Typen verwenden. EF erstellt dann nahtlos das entsprechende Datenbankschema, erstellt Abfragen 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.

Zuordnung der Entitätstyphierarchie

Standardmäßig sucht EF nicht automatisch nach grundlegenden oder abgeleiteten Typen. Dies bedeutet, dass Sie diesen Typ in Ihrem Modell explizit angeben müssen, wenn ein CLR-Typ in Ihrer Hierarchie zugeordnet werden soll. Wenn Sie beispielsweise 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 über eine andere Unterklasse verfügt, wird sie nicht in das Modell einbezogen.

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. Beispielsweise lässt die Spalte RssUrl Nullwerte zu, da normale Blog-Instanzen nicht über diese Eigenschaft verfügen.

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

Tipp

Wenn Sie sich nicht auf Konventionen verlassen, können Sie den Basistyp explizit mithilfe von HasBaseType angeben. Sie können auch .HasBaseType((Type)null) verwenden, um einen Entitätstyp aus der Hierarchie zu 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 Spalte Discriminator, die angibt, welcher Typ von Blog in jeder Zeile gespeichert ist).

Screenshot of the results of querying the Blog entity hierarchy using table-per-hierarchy pattern

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 der Basisentität der Hierarchie hinzugefügt. Diese Eigenschaft kann wie jede andere konfiguriert werden:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property("Discriminator")
        .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 wir beim Materialisieren der Ergebnisse einer Abfrage auf einen Diskriminatorwert stoßen, der keinem Entitätstyp im Modell zugeordnet ist, lösen wir eine Ausnahme aus, da wir nicht wissen, wie wir die Ergebnisse materialisieren sollen. Dieser Fehler tritt nur auf, wenn Ihre Datenbank Zeilen mit Diskriminatorwerten enthält, die nicht im EF-Modell zugeordnet sind. Wenn Sie solche Daten haben, können Sie die Diskriminator-Zuordnung im EF Core-Modell als unvollständig markieren, um anzuzeigen, dass wir immer ein 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, z. B. Url = blog is RssBlog ? (blog as RssBlog).Url : null.

Tabelle-pro-Typ-Konfiguration

Im TPT-Zuordnungsmuster werden alle Typen einzelnen Tabellen zugeordnet. Eigenschaften, die nur zu einem Basistyp oder einem abgeleiteten 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

Anstatt ToTable für jeden Entitätstyp aufzurufen, können Sie modelBuilder.Entity<Blog>().UseTptMappingStrategy() für jeden Stammentitätstyp aufrufen, 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 obige 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 angewandt, die der Hierarchie zugeordnet sind. Zukünftige EF-Versionen werden die Umbenennung der Beschränkung nur für eine bestimmte Tabelle erlauben, wenn das Problem 19970 behoben ist.

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

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 der Dokumentation zur Leistung.

Achtung

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)

Hinweis

Das TPC-Feature (Tabelle pro konkreter Typ) wurde in EF Core 7.0 eingeführt.

Im TPT-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 demonstrierte und sprach ausführlich über die TPC-Zuordnung 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

Anstatt ToTable für jeden Entitätstyp aufzurufen, genügt es, modelBuilder.Entity<Blog>().UseTpcMappingStrategy() für jeden Stamm-Entitätstyp aufzurufen, um die Tabellennamen per Konvention zu erzeugen.

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 obige 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])
);

Das 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 nicht für abstrakte Typen erstellt werden – daher der Name „Tabelle pro konkretem Typ“. 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-Datenbankschematas 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 abstract sind. Denken Sie daran, dass C# keine Instanzen abstrakter Typen zulässt, und es daher keine Situation gibt, 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:

Katzentabelle

Id Name FoodID Tierarzt Bildungsgrad
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 Haustierkrankenhaus Bothell BSc

Hundetabelle

Id Name FoodID Tierarzt Lieblingsspielzeug
3 Toast 011aaf6f-d588-4fad-d4ac-08da7aca624f Pengelly Mr. Squirrel

Bauernhoftiere-Tabelle

Id Name FoodID Wert Art
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.

Bei TPC werden die Dinge etwas komplizierter. Zunächst ist es wichtig zu verstehen, dass EF Core erfordert, dass alle Entitäten in einer Hierarchie über einen eindeutigen Schlüsselwert verfügen, auch wenn die Entitäten unterschiedliche Typen aufweisen. Mit unserem Beispielmodell kann ein Hund also 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.

Obwohl Standardidentitätsspalten nicht mit TPC funktionieren, ist es dennoch möglich, Identitätsspalten zu verwenden, wenn jede Tabelle mit einem geeigneten Seed 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, später abgeleitete Typen hinzuzufügen, da die Gesamtanzahl der Typen in der Hierarchie vorher bekannt sein muss.

SQLite unterstützt keine Sequenzen oder Identity Seeds/Increments, 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 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 ein Tier in der Tabelle für den konkreten Typ dieses Tieres gespeichert. 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 und Navigationen zum Verknüpfen von Entitäten verwendet werden, wird sichergestellt, dass die FK-Spalte jederzeit einen gültigen PK-Wert enthält.

Zusammenfassung und Orientierungshilfe

Zusammenfassend lässt sich feststellen, dass TPH in der Regel für die meisten Anwendungen in Ordnung und ein guter Standardwert für eine Vielzahl von Szenarien ist, sodass Sie die Komplexität von TPC nicht unnötigerweise hinzufügen müssen. Wenn Ihr Code hauptsächlich Entitäten vieler Typen abfragt, z. B. Abfragen für den Basistyp, dann sollten Sie TPH gegenüber TPC bevorzugen.

Davon abgesehen ist TPC auch eine gute Mapping-Strategie, wenn Ihr Code hauptsächlich nach Entitäten eines einzigen Blatttyps sucht und Ihre Benchmarks eine Verbesserung gegenüber TPH zeigen.

Verwenden Sie TPT nur, wenn dies durch externe Faktoren vorgegeben wird.