Entitätstypen

Das Einschließen eines DbSet-Typs in Ihren Kontext bedeutet, dass er im Modell von EF Core enthalten ist; in der Regel beziehen wir uns auf einen solchen Typ als Entität. EF Core kann Entitätsinstanzen aus der Datenbank lesen und dort hinein schreiben, und wenn Sie eine relationale Datenbank verwenden, kann EF Core Tabellen für Ihre Entitäten über Migrationen erstellen.

Einschließen von Typen im Modell

Standardmäßig werden Typen, die in DbSet-Eigenschaften in Ihrem Kontext verfügbar gemacht werden, als Entitäten in das Modell eingeschlossen. In der OnModelCreating-Methode angegebene Entitätstypen werden ebenfalls eingeschlossen, ebenso wie alle Typen, die durch rekursives Untersuchen der Navigationseigenschaften anderer ermittelter Entitätstypen gefunden werden.

Im folgenden Codebeispiel sind alle Typen eingeschlossen:

  • Blog ist eingeschlossen, da er in einer DbSet-Eigenschaft im Kontext verfügbar gemacht wird.
  • Post ist eingeschlossen, da er über die Blog.Posts-Navigationseigenschaft ermittelt wird.
  • AuditEntry, da er in OnModelCreating angegeben wird.
internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AuditEntry>();
    }
}

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

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

public class AuditEntry
{
    public int AuditEntryId { get; set; }
    public string Username { get; set; }
    public string Action { get; set; }
}

Ausschließen von Typen aus dem Modell

Wenn Sie nicht möchten, dass ein Typ im Modell eingeschlossen ist, können Sie ihn ausschließen:

[NotMapped]
public class BlogMetadata
{
    public DateTime LoadedFromDatabase { get; set; }
}

Ausschließen von Migrationen

Es ist manchmal nützlich, denselben Entitätstyp in mehreren DbContext-Typen zugeordnet zu haben. Dies ist vor allem dann der Fall, wenn Kontextgrenzen verwendet werden, bei denen üblicherweise für jede Kontextgrenze ein anderer DbContext-Typ verwendet wird.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}

Bei diesen Konfigurationsmigrationen wird die AspNetUsers-Tabelle nicht erstellt, aber IdentityUser ist weiterhin im Modell eingeschlossen und kann normal verwendet werden.

Wenn Sie erneut mit der Verwaltung der Tabelle mit Migrationen beginnen müssen, sollte eine neue Migration erstellt werden, in der AspNetUsers nicht ausgeschlossen ist. Die nächste Migration enthält nun alle Änderungen, die an der Tabelle vorgenommen wurden.

Tabellenname

Gemäß der Konvention wird jeder Entitätstyp so eingerichtet, dass er einer Datenbanktabelle mit dem gleichen Namen wie die DbSet-Eigenschaft zugeordnet ist, die die Entität verfügbar macht. Wenn kein DbSet für die angegebene Entität vorhanden ist, wird der Klassenname verwendet.

Sie können den Tabellennamen manuell konfigurieren:

[Table("blogs")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Tabellenschema

Wenn Sie eine relationale Datenbank verwenden, werden Tabellen gemäß der Konvention im Standardschema Ihrer Datenbank erstellt. Beispielsweise verwendet Microsoft SQL Server das dbo-Schema (SQLite unterstützt keine Schemas).

Sie können Tabellen wie folgt so konfigurieren, dass sie in einem bestimmten Schema erstellt werden:

[Table("blogs", Schema = "blogging")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Anstatt das Schema für jede Tabelle anzugeben, können Sie auch das Standardschema auf Modellebene mit der Fluent-API definieren:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasDefaultSchema("blogging");
}

Beachten Sie, dass sich das Festlegen des Standardschemas auch auf andere Datenbankobjekte auswirkt, z. B. Sequenzen.

Ansichtszuordnung

Entitätstypen können mithilfe der Fluent-API Datenbankansichten zugeordnet werden.

Hinweis

EF geht davon aus, dass die referenzierte Ansicht bereits in der Datenbank vorhanden ist, sie wird nicht automatisch in einer Migration erstellt.

modelBuilder.Entity<Blog>()
    .ToView("blogsView", schema: "blogging");

Durch die Zuordnung zu einer Ansicht wird die Standardtabellenzuordnung entfernt, der Entitätstyp kann aber auch explizit einer Tabelle zugeordnet werden. In diesem Fall wird die Abfragezuordnung für Abfragen und die Tabellenzuordnung für Updates verwendet.

Tipp

Um schlüssellose Entitätstypen zu testen, die Ansichten mithilfe des In-Memory-Anbieters zugeordnet sind, ordnen Sie sie über ToInMemoryQuery einer Abfrage zu. Weitere Informationen finden Sie in den Dokumentationen zum In-Memory-Anbieter.

Zuordnung von Tabellenwertfunktionen

Es ist möglich, einen Entitätstyp einer Tabellenwertfunktion (Table-Valued Function, TVF) anstelle einer Tabelle in der Datenbank zuzuordnen. Um dies zu veranschaulichen, definieren wir eine andere Entität, die einen Blog mit mehreren Beiträgen darstellt. Im Beispiel ist die Entität schlüssellos, dies muss aber nicht sein.

public class BlogWithMultiplePosts
{
    public string Url { get; set; }
    public int PostCount { get; set; }
}

Erstellen Sie als Nächstes die folgende Tabellenwertfunktion in der Datenbank, die nur Blogs mit mehreren Beiträgen sowie die Anzahl der Beiträge zurückgibt, die jedem dieser Blogs zugeordnet sind:

CREATE FUNCTION dbo.BlogsWithMultiplePosts()
RETURNS TABLE
AS
RETURN
(
    SELECT b.Url, COUNT(p.BlogId) AS PostCount
    FROM Blogs AS b
    JOIN Posts AS p ON b.BlogId = p.BlogId
    GROUP BY b.BlogId, b.Url
    HAVING COUNT(p.BlogId) > 1
)

Nun kann die Entität BlogWithMultiplePosts auf folgende Weise dieser Funktion zugeordnet werden:

modelBuilder.Entity<BlogWithMultiplePosts>().HasNoKey().ToFunction("BlogsWithMultiplePosts");

Hinweis

Damit eine Entität einer Tabellenwertfunktion zugeordnet werden kann, muss die Funktion parameterlos sein.

Üblicherweise werden die Entitätseigenschaften übereinstimmenden Spalten zugeordnet, die von der TVF zurückgegeben werden. Wenn die von der TVF zurückgegebenen Spalten andere Namen als die Entitätseigenschaft haben, können die Spalten der Entität mithilfe der HasColumnName-Methode konfiguriert werden, genau wie bei der Zuordnung zu einer regulären Tabelle.

Wenn der Entitätstyp einer Tabellenwertfunktion zugeordnet ist, erfolgt die Abfrage folgendermaßen:

var query = from b in context.Set<BlogWithMultiplePosts>()
            where b.PostCount > 3
            select new { b.Url, b.PostCount };

Dies generiert diese SQL-Anweisung:

SELECT [b].[Url], [b].[PostCount]
FROM [dbo].[BlogsWithMultiplePosts]() AS [b]
WHERE [b].[PostCount] > 3

Tabellenkommentare

Sie können einen beliebigen Textkommentar festlegen, der in der Datenbanktabelle festgelegt wird, sodass Sie Ihr Schema in der Datenbank dokumentieren können:

[Comment("Blogs managed on the website")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Gemeinsame Entitätstypen

Entitätstypen, die denselben CLR-Typ verwenden, werden als gemeinsame Entitätstypen bezeichnet. Diese Entitätstypen müssen mit einem eindeutigen Namen konfiguriert werden, der immer dann angegeben werden muss, wenn der Entitätstyp gemeinsam verwendet wird, zusätzlich zum CLR-Typ. Dies bedeutet, dass die entsprechende DbSet-Eigenschaft mithilfe eines Set-Aufrufs implementiert werden muss.

internal class MyContext : DbContext
{
    public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
            "Blog", bb =>
            {
                bb.Property<int>("BlogId");
                bb.Property<string>("Url");
                bb.Property<DateTime>("LastUpdated");
            });
    }
}