Scaffolding (反向工程)
反向工程是以資料庫架構為基礎的 Scaffolding 實體類型類別和 DbContext 類別的程式。 您可以使用 EF Core 套件管理員主控台 (PMC) 工具的 Scaffold-DbContext
命令,或 .NET 命令列介面 (CLI) 工具的 dotnet ef dbcontext scaffold
命令來執行還原工程。
注意
這裡記載的 DbContext
和 實體類型的 Scaffold 與使用 Visual Studio ASP.NET Core 中控制器的 Scaffold 不同,此處並未記載。
提示
如果您使用 Visual Studio,請嘗試 EF Core Power Tools 社群延伸模組。 這些工具提供圖形化工具,其建置在 EF Core 命令行工具之上,並提供額外的工作流程和自定義選項。
必要條件
- 在 Scaffolding 之前,您必須安裝 PMC 工具,其僅適用於 Visual Studio,或 .NET CLI 工具,這些工具橫跨 .NET 支援的所有平臺。
- 在您要 Scaffold 至的專案中安裝
Microsoft.EntityFrameworkCore.Design
適用的 NuGet 套件。 - 安裝以您要從中 Scaffold 之資料庫架構為目標之資料庫提供者的 NuGet 套件。
必要引數
PMC 和 .NET CLI 命令都有兩個必要自變數:資料庫 連接字串,以及要使用的 EF Core 資料庫提供者。
Connection string
警告
本文使用本機資料庫,其不需要使用者進行驗證。 實際執行應用程式應該使用可用的最安全驗證流程。 如需已部署測試與實際執行應用程式驗證的詳細資訊,請參閱安全驗證流程。
命令的第一個引數是資料庫的連接字串。 這些工具會使用此 連接字串 來讀取資料庫架構。
連接字串的引號和逸出方式取決於用來執行命令的殼層。 請參閱殼層的檔。 例如,PowerShell 需要逸出 $
,但不需要 \
。
下列範例會從Chinook
位於電腦 SQL Server LocalDB 實體例的資料庫,使用資料庫提供者,Microsoft.EntityFrameworkCore.SqlServer
建立實體類型和 DbContext
。
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
提示
您可以使用組態來儲存和擷取 連接字串
Scaffolded 程式代碼中的連接字串
根據預設,Scaffolder 會在 Scaffolded 程式代碼中包含 連接字串,但出現警告。 例如:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Database=AllTogetherNow");
如此一來,產生的程式代碼就不會在第一次使用時當機,這將會是非常糟糕的學習體驗。 不過,如警告所示,連接字串 不應該存在於生產程序代碼中。 如需可管理 連接字串的各種方式,請參閱 DbContext 存留期、設定和初始化。
提示
-NoOnConfiguring
可以傳遞 (Visual Studio PMC) 或 --no-onconfiguring
(.NET CLI) 選項,以抑制建立OnConfiguring
包含 連接字串 的方法。
提供者名稱
第二個引數是提供者名稱。 提供者名稱通常會與提供者的 NuGet 套件名稱相同。 例如,針對 SQL Server 或 Azure SQL,請使用 Microsoft.EntityFrameworkCore.SqlServer
。
命令列選項
Scaffolding 程式可由各種命令行選項控制。
指定數據表和檢視
根據預設,資料庫架構中的所有數據表和檢視都會建構成實體類型。 您可以藉由指定架構和數據表來限制要建立的數據表和檢視表。
-Schemas
(Visual Studio PMC) 或 --schema
(.NET CLI) 自變數會指定將產生實體類型的數據表和檢視架構。 如果省略此自變數,則會包含所有架構。 如果使用此選項,則即使未使用 或 --table
明確包含架構中的所有數據表和檢視,架構中的所有數據表和檢視也會包含在-Tables
模型中。
-Tables
(Visual Studio PMC) 或 --table
(.NET CLI) 自變數會指定將產生實體類型的數據表和檢視。 特定架構中的數據表或檢視表可以使用 'schema.table' 或 'schema.view' 格式來包含。 如果省略此選項,則會包含所有數據表和檢視表。 |
例如,若要只 Artists
建立 和 Albums
數據表的 Scaffold:
dotnet ef dbcontext scaffold ... --table Artist --table Album
若要從和 Contractor
架構建立所有資料表和檢視Customer
表:
dotnet ef dbcontext scaffold ... --schema Customer --schema Contractor
例如,若要從架構建立Purchases
數據表,以及Accounts
架構中的 Contractor
和 Contracts
數據表:Customer
dotnet ef dbcontext scaffold ... --table Customer.Purchases --table Contractor.Accounts --table Contractor.Contracts
保留資料庫名稱
資料表和資料行名稱預設會進行修正,以更符合類型和屬性的 .NET 命名慣例。 -UseDatabaseNames
指定 (Visual Studio PMC) 或 --use-database-names
(.NET CLI) 將會停用此行為,盡可能保留原始資料庫名稱。 此外,無效的 .NET 識別碼仍會進行修正,而導覽屬性等合成名稱仍會符合 .NET 命名慣例。
例如,請考慮下列數據表:
CREATE TABLE [BLOGS] (
[ID] int NOT NULL IDENTITY,
[Blog_Name] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY ([ID]));
CREATE TABLE [posts] (
[id] int NOT NULL IDENTITY,
[postTitle] nvarchar(max) NOT NULL,
[post content] nvarchar(max) NOT NULL,
[1 PublishedON] datetime2 NOT NULL,
[2 DeletedON] datetime2 NULL,
[BlogID] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogID]) REFERENCES [Blogs] ([ID]) ON DELETE CASCADE);
根據預設,下列實體類型會從這些數據表進行 Scaffold:
public partial class Blog
{
public int Id { get; set; }
public string BlogName { get; set; } = null!;
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}
public partial class Post
{
public int Id { get; set; }
public string PostTitle { get; set; } = null!;
public string PostContent { get; set; } = null!;
public DateTime _1PublishedOn { get; set; }
public DateTime? _2DeletedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}
不過,使用 -UseDatabaseNames
或 --use-database-names
會產生下列實體類型:
public partial class BLOG
{
public int ID { get; set; }
public string Blog_Name { get; set; } = null!;
public virtual ICollection<post> posts { get; set; } = new List<post>();
}
public partial class post
{
public int id { get; set; }
public string postTitle { get; set; } = null!;
public string post_content { get; set; } = null!;
public DateTime _1_PublishedON { get; set; }
public DateTime? _2_DeletedON { get; set; }
public int BlogID { get; set; }
public virtual BLOG Blog { get; set; } = null!;
}
使用對應屬性 (也稱為資料批註)
根據預設, ModelBuilder
實體類型會使用 中的 OnModelCreating
API 進行設定。 指定 -DataAnnotations
(PMC) 或 --data-annotations
(.NET Core CLI) 以盡可能改用 對應屬性 。
例如,使用 Fluent API 會 Scaffold 下列內容:
entity.Property(e => e.Title)
.IsRequired()
.HasMaxLength(160);
而使用資料註解則會 Scaffold 下列內容:
[Required]
[StringLength(160)]
public string Title { get; set; }
提示
無法使用對應屬性來設定模型的某些層面。 Scaffolder 仍會使用模型建置 API 來處理這些案例。
DbCoNtext 名稱
Scaffolded DbContext
類別名稱預設會是以 Context 後綴的資料庫名稱。 若要指定不同的名稱,請在 PMC 中使用 -Context
,以及在 .NET Core CLI 中使用 --context
。
目標目錄和命名空間
實體類別和 DbCoNtext 類別會 Scaffold 到專案的根目錄中,並使用專案的預設命名空間。
您可以使用 --output-dir
指定類別的 Scaffold 位置所在目錄,而 --context-dir
可用來將 DbCoNtext 類別 Scaffold 到與實體類型類別不同的目錄:
dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models
命名空間預設會是根命名空間加上專案根目錄下任何子目錄的名稱。 不過,您可以使用 來覆寫所有輸出類別的 --namespace
命名空間。 您也可以使用 --context-namespace
,只針對 DbCoNtext 類別覆寫命名空間:
dotnet ef dbcontext scaffold ... --namespace Your.Namespace --context-namespace Your.DbContext.Namespace
Scaffold 程式代碼
從現有資料庫進行 Scaffolding 的結果為:
- 檔案,其中包含繼承自的類別
DbContext
- 每個實體類型的檔案
提示
從 EF7 開始,您也可以使用 T4 文字範本來自訂產生的程式碼。 如需詳細資訊,請參閱自訂還原工程範本。
C# 可為 Null 的參考型別
Scaffolder 可以建立 EF 模型和實體類型,以使用 C# 可為 Null 的參考型 別 (NRT)。 在 C# 專案中啟用 NRT 支援時,NRT 使用方式會自動進行 Scaffold 處理。
例如,下表 Tags
同時包含可為 Null 的不可為 Null 字串資料列:
CREATE TABLE [Tags] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));
這會導致產生的類別中對應的可為 Null 和不可為 Null 的字串屬性:
public partial class Tag
{
public Tag()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
同樣地,下表 Posts
包含數據表的必要關聯性 Blogs
:
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NOT NULL,
[Contents] nvarchar(max) NOT NULL,
[PostedOn] datetime2 NOT NULL,
[UpdatedOn] datetime2 NULL,
[BlogId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]));
這會導致部落格之間不可為 Null 的 (必要) 關聯性 Scaffold:
public partial class Blog
{
public Blog()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public virtual ICollection<Post> Posts { get; set; }
}
文章:
public partial class Post
{
public Post()
{
Tags = new HashSet<Tag>();
}
public int Id { get; set; }
public string Title { get; set; } = null!;
public string Contents { get; set; } = null!;
public DateTime PostedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; }
}
多對多關聯性
Scaffolding 程式會偵測簡單的聯結數據表,並自動為其產生 多對多對應 。 例如,請考慮 和Tags
的Posts
數據表,以及連接它們的聯結數據表PostTag
:
CREATE TABLE [Tags] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NOT NULL,
[Contents] nvarchar(max) NOT NULL,
[PostedOn] datetime2 NOT NULL,
[UpdatedOn] datetime2 NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]));
CREATE TABLE [PostTag] (
[PostsId] int NOT NULL,
[TagsId] int NOT NULL,
CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
CONSTRAINT [FK_PostTag_Posts_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_PostTag_Tags_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE);
Scaffolded 時,這會產生 Post 的類別:
public partial class Post
{
public Post()
{
Tags = new HashSet<Tag>();
}
public int Id { get; set; }
public string Title { get; set; } = null!;
public string Contents { get; set; } = null!;
public DateTime PostedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; }
}
以及 Tag 的類別:
public partial class Tag
{
public Tag()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
但是數據表沒有類別 PostTag
。 相反地,會建立多對多關聯性的組態:
entity.HasMany(d => d.Tags)
.WithMany(p => p.Posts)
.UsingEntity<Dictionary<string, object>>(
"PostTag",
l => l.HasOne<Tag>().WithMany().HasForeignKey("PostsId"),
r => r.HasOne<Post>().WithMany().HasForeignKey("TagsId"),
j =>
{
j.HasKey("PostsId", "TagsId");
j.ToTable("PostTag");
j.HasIndex(new[] { "TagsId" }, "IX_PostTag_TagsId");
});
其他程式設計語言
Microsoft Scaffold C# 程式代碼所發行的 EF Core 套件。 不過,基礎 Scaffolding 系統支援外掛程式模型來建構成其他語言。 此外掛程式模型由各種社群執行專案使用,例如:
- EntityFrameworkCore.VisualBasic 提供 Visual Basic 的支援
- EFCore.FSharp 提供 F 的支援#
自訂程序代碼
從EF7開始,自定義所產生程序代碼的最佳方式之一是 自定義用來產生它的 T4 範本。
程式代碼在產生之後也可以變更,但執行此動作的最佳方式取決於您是否打算在資料庫模型變更時重新執行 Scaffolding 程式。
僅 Scaffold 一次
使用此方法,Scaffold 程式代碼會提供程式碼型對應的起點。 您可以視需要對產生的程式代碼進行任何變更,使其變成一般程式代碼,就像專案中的任何其他程式碼一樣。
您可以透過下列兩種方式之一,讓資料庫和 EF 模型保持同步:
- 切換至使用 EF Core 資料庫移轉,並使用實體類型和 EF 模型組態作為事實來源,使用移轉來驅動架構。
- 當資料庫變更時,手動更新實體類型和 EF 組態。 例如,如果新的數據行加入至數據表,則將數據行的屬性新增至對應的實體類型,並使用 中的
OnModelCreating
對應屬性和/或程式代碼新增任何必要的組態。 這相當容易,唯一真正的挑戰是一個程式,以確保以某種方式記錄或偵測資料庫變更,讓負責程式代碼的開發人員可以做出反應。
重複的 Scaffolding
一次 Scaffolding 的替代方法是每次資料庫變更時重新建立 Scaffold。 這會覆寫任何先前建構的程序代碼,這表示對實體類型或 EF 組態所做的任何變更都會遺失。
[TIP]根據預設,EF 命令不會覆寫任何現有的程序代碼,以防止意外遺失程序代碼。
-Force
(Visual Studio PMC) 或--force
(.NET CLI) 自變數可用來強制覆寫現有的檔案。
由於會覆寫 Scaffold 程式代碼,因此最好不要直接修改它,而是依賴 部分類別和方法,以及 EF Core 中允許覆寫組態的機制。 具體而言:
- 類別
DbContext
和實體類別都會產生為部分。 這允許在個別的檔案中引進其他成員和程序代碼,在 Scaffolding 執行時不會覆寫。 - 類別
DbContext
包含稱為OnModelCreatingPartial
的部分方法。 這個方法的實作可以加入 至 的部分類別。DbContext
接著會在呼叫 之後OnModelCreating
呼叫它。 - 使用 API 建立的
ModelBuilder
模型組態會覆寫慣例或對應屬性所完成的任何組態,以及模型產生器上先前完成的設定。 這表示 中的OnModelCreatingPartial
程式代碼可用來覆寫 Scaffolding 程式所產生的組態,而不需要移除該組態。
最後,請記住,從EF7開始,您可以自定義用來產生程式碼的 T4 範本。 這通常比使用預設值的 Scaffolding 更有效率的方法,然後使用部分類別和/或方法進行修改。
運作方式
還原工程會從讀取資料庫結構描述開始。 其會讀取資料表、資料行、條件約束和索引的相關資訊。
接著,還原工程會使用結構描述的資訊來建立 EF Core 模型。 資料表會用於建立實體類型;資料行用於建立屬性;而外部索引鍵或用於建立關聯性。
最後,模型會用於產生程式碼。 為了重新建立與您應用程式相同的模型,會 Scaffold 對應的實體類型類別、Fluent API 和資料註解。