實體類型

在您的內容中包含類型的 DbSet 表示它包含在 EF Core 的模型中;我們通常會將這類類型 稱為實體 。 EF Core 可以從資料庫讀取和寫入實體實例,而且如果您使用關係資料庫,EF Core 可以透過移轉為實體建立資料表。

在模型中包括類型

依照慣例,在內容上的 DbSet 屬性中公開的類型會以實體的形式包含在模型中。 方法中指定的 OnModelCreating 實體類型也包含在內,以及遞迴探索其他探索實體類型的導覽屬性所找到的任何類型。

在下列程式碼範例中,會包含所有類型:

  • Blog 包含 ,因為它會在內容上的 DbSet 屬性中公開。
  • Post 因為透過導覽屬性探索到 Blog.Posts 它,所以會包含它。
  • AuditEntry 因為它在 中 OnModelCreating 指定。
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; }
}

從模型排除類型

如果您不想在模型中包含類型,則可以將其排除:

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

從移轉排除

有時候,在多個 DbContext 類型中對應相同的實體類型會很有用。 使用系結內容時 ,這尤其如此,每個限定內容 通常會有不同的 DbContext 類型。

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

使用此組態移轉不會建立 AspNetUsers 資料表,但仍 IdentityUser 包含在模型中,而且可以正常使用。

如果您需要再次使用移轉開始管理資料表,則應該在未排除的位置 AspNetUsers 建立新的移轉。 下一個移轉現在會包含對資料表所做的任何變更。

資料表名稱

依照慣例,每個實體類型都會設定為對應至與公開實體之 DbSet 屬性同名的資料庫資料表。 如果指定的實體不存在 DbSet,則會使用類別名稱。

您可以手動設定資料表名稱:

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

資料表架構

使用關係資料庫時,資料表會依慣例在資料庫的預設架構中建立。 例如,Microsoft SQL Server 將使用 dbo 架構(SQLite 不支援架構)。

您可以將資料表設定為在特定架構中建立,如下所示:

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

您也可以使用 Fluent API 在模型層級定義預設架構,而不是為每個資料表指定架構:

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

請注意,設定預設架構也會影響其他資料庫物件,例如序列。

檢視對應

實體類型可以使用 Fluent API 對應至資料庫檢視。

注意

EF 會假設參考的檢視已存在於資料庫中,因此不會在移轉中自動建立檢視。

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

對應至檢視將會移除預設資料表對應,但實體類型也可以明確地對應至資料表。 在此情況下,查詢對應將用於查詢,而資料表對應將用於更新。

提示

若要使用記憶體內部提供者測試對應至檢視的無索引鍵實體類型,請透過 ToInMemoryQuery 將其對應至查詢。 如需詳細資訊,請參閱記憶體內部提供者檔

資料表值函式對應

可以將實體類型對應至資料表值函式 (TVF),而不是資料庫中的資料表。 為了說明這一點,讓我們定義另一個實體,代表具有多個文章的部落格。 在此範例中,實體是 索引鍵的,但不需要。

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

接下來,在資料庫中建立下列資料表值函式,只傳回具有多個文章的部落格,以及與每個部落格相關聯的文章數目:

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
)

現在,實體 BlogWithMultiplePosts 可以透過下列方式對應至此函式:

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

注意

若要將實體對應至資料表值函式,函式必須是無參數的。

傳統上,實體屬性會對應到 TVF 所傳回的相符資料行。 如果 TVF 傳回的資料行名稱與實體屬性不同,則可以使用 HasColumnName 方法設定實體的資料行,就像對應至一般資料表一樣。

當實體類型對應至資料表值函式時,查詢:

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

產生下列 SQL:

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

資料表批註

您可以設定在資料庫資料表上設定的任意文字批註,讓您在資料庫中記錄架構:

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

共用類型實體類型

使用相同 CLR 類型的實體類型稱為共用類型實體類型。 除了 CLR 類型之外,這些實體類型還需要以唯一的名稱進行設定,每當使用共用類型實體類型時,都必須提供此名稱。 這表示必須使用呼叫實 Set 作對應的 DbSet 屬性。

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");
            });
    }
}