自訂反向工程範本
注意
這項功能已在 EF Core 7 中新增。
雖然 反向工程 ,Entity Framework Core 會努力建立良好的一般用途程式碼,這些程式碼可用於各種應用程式類型,並使用 常見的程式碼慣例 ,以保持一致的外觀和熟悉的感覺。 不過,有時候,更特殊的程式碼和替代程式碼樣式是可取的。 本文說明如何使用 T4 文字模板 自訂 Scaffold 程式碼 。
必要條件
本文假設您已熟悉 EF Core 中的反向工程。 如果沒有,請先檢閱該文章再繼續進行。
新增預設範本
自訂 Scaffold 程式碼的第一個步驟是將預設範本新增至您的專案。 預設範本是 EF Core 在內部進行反向工程時所使用的範本。 它們提供起點,讓您開始自訂 Scaffolded 程式碼。
首先,安裝 的 dotnet new
EF Core 範本套件:
dotnet new install Microsoft.EntityFrameworkCore.Templates
現在您可以將預設範本新增至專案。 請從您的專案目錄執行下列命令,以執行此動作。
dotnet new ef-templates
此命令會將下列檔案新增至您的專案。
- CodeTemplates/
- EFCore/
- DbCoNtext.t4
- EntityType.t4
- EFCore/
此 DbContext.t4
範本可用來為資料庫建構 DbCoNtext 類別,而 EntityType.t4
範本則用來針對資料庫中每個資料表和檢視表建立實體類型類別。
提示
.t4 副檔名是用來防止 Visual Studio 轉換範本。 範本會改為由 EF Core 轉換。
T4 簡介
讓我們開啟 DbContext.t4
範本並檢查其內容。 此檔案是 T4 文字模板 。 T4 是使用 .NET 產生文字的語言。 下列程式碼僅供說明之用;它不代表檔案的完整內容。
重要
T4 文字模板,特別是產生程式碼的範本,在不使用語法醒目提示的情況下,可能很難閱讀。 如有必要,請搜尋程式碼編輯器的延伸模組,以啟用 T4 語法醒目提示。
<#@ template hostSpecific="true" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ parameter name="NamespaceHint" type="System.String" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
<#
if (!string.IsNullOrEmpty(NamespaceHint))
{
#>
namespace <#= NamespaceHint #>;
開頭 <#@
的前幾行稱為 指示詞。 它們會影響範本的轉換方式。 下表簡短描述所使用的每種指示詞。
指示詞 | 描述 |
---|---|
template |
指定 hostSpecific=「true」 ,讓使用 Host 範本內的 屬性來存取 EF Core 服務。 |
assembly |
新增編譯範本所需的元件參考。 |
parameter |
宣告轉換範本時由 EF Core 傳入的參數。 |
import |
如同 C# using 指示詞,將命名空間帶入範本程式碼的範圍。 |
指示詞之後,的 DbContext.t4
下一個區段稱為控制區塊。 標準控制項區塊開頭為 <#
,結尾為 #>
。 轉換範本時,將會執行其內的程式碼。 如需控制項區塊內可用的屬性和方法清單,請參閱 TextTransformation 類別。
控制項區塊以外的任何專案都會直接複製到範本輸出。
運算式控制項區塊開頭為 <#=
。 其內的程式碼將會進行評估,並將結果新增至範本輸出。 這些類似于 C# 插補字串引數。
如需 T4 語法的更詳細和完整說明,請參閱 撰寫 T4 文字模板 。
自訂實體類型
讓我們逐步解說自訂範本的樣子。 根據預設,EF Core 會針對集合導覽屬性產生下列程式碼。
public virtual ICollection<Album> Albums { get; } = new List<Album>();
使用 List<T>
是大部分應用程式的良好預設值。 不過,如果您使用 XAML 架構,例如 WPF、WinUI 或 .NET MAUI,通常想要改用 ObservableCollection<T>
來啟用資料系結。
EntityType.t4
開啟範本,並尋找產生 List<T>
的位置。 畫面顯示為這樣:
if (navigation.IsCollection)
{
#>
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new List<<#= targetType #>>();
<#
}
將 List 取代為 ObservableCollection。
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new ObservableCollection<<#= targetType #>>();
我們也必須將 指示詞新增 using
至 Scaffolded 程式碼。 using 是在範本頂端附近的清單中指定。 新增 System.Collections.ObjectModel
至清單。
var usings = new List<string>
{
"System",
"System.Collections.Generic",
"System.Collections.ObjectModel"
};
使用反向工程命令來測試變更。 命令會自動使用專案內的範本。
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
如果您先前已執行命令,請新增 --force
選項以覆寫現有的檔案。
如果您已正確執行所有作業,集合導覽屬性現在應該使用 ObservableCollection<T>
。
public virtual ICollection<Album> Albums { get; } = new ObservableCollection<Album>();
更新範本
當您將預設範本新增至專案時,它會根據該版本的 EF Core 建立其複本。 由於 Bug 已修正,且會在後續 EF Core 版本中新增功能,您的範本可能會過期。 您應該檢閱 EF Core 範本中所做的變更,並將其合併到您的自訂範本中。
檢閱對 EF Core 範本所做的變更的其中一種方式,就是使用 git 來比較版本之間的變更。 下列命令會複製 EF Core 存放庫,並在 7.0.0 和 8.0.0 版之間產生這些檔案的差異。
git clone --no-checkout https://github.com/dotnet/efcore.git
cd efcore
git diff v7.0.0 v8.0.0 -- src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.tt
檢閱變更的另一種方式是從 NuGet 下載兩個 版本的 Microsoft.EntityFrameworkCore.Templates 、擷取其內容(您可以將副檔名變更為 .zip),以及比較這些檔案。
將預設範本新增至新專案之前,請記得更新至最新的 EF Core 範本套件。
dotnet new update
進階使用方式
忽略輸入模型
Model
和 EntityType
參數代表與資料庫對應的其中一種可能方式。 您可以選擇忽略或變更模型的元件。 例如,我們提供的導覽名稱可能不理想,而且您可以在建構程式碼時將其取代為您自己的名稱。 其他像是條件約束名稱和索引篩選等專案只會由移轉使用,如果您不想要搭配 Scaffold 程式碼使用移轉,則可以安全地從模型省略。 同樣地,如果應用程式未使用序列或預設條件約束,您可能會想要省略這些序列或預設條件約束。
進行這樣的進階變更時,請確定產生的模型與資料庫保持相容。 檢閱 所產生的 dbContext.Database.GenerateCreateScript()
SQL 是驗證此專案的好方法。
實體組態類別
對於大型模型,DbCoNtext 類別的 OnModelCreating 方法可能會變得難以管理。 解決這個問題的其中一種方法是使用 IEntityTypeConfiguration<T>
類別。 如需這些類別的詳細資訊,請參閱 建立和設定模型 。
若要建構這些類別,您可以使用名為 EntityTypeConfiguration.t4
的第三個範本。 EntityType.t4
如同範本,它會用於模型中的每個實體類型,並使用 EntityType
範本參數。
Scaffolding 其他類型的檔案
EF Core 中反向工程的主要目的是建立 DbCoNtext 和實體類型。 不過,工具中沒有任何需要您實際建構程式碼的工具。 例如,您可以改用 美人魚 來建立實體關聯性圖表。
<#@ output extension=".md" #>
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ parameter name="Model" type="Microsoft.EntityFrameworkCore.Metadata.IModel" #>
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
# <#= Options.ContextName #>
```mermaid
erDiagram
<#
foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
{
#>
<#= entityType.Name #> {
}
<#
foreach (var foreignKey in entityType.GetForeignKeys())
{
#>
<#= entityType.Name #> <#= foreignKey.IsUnique ? "|" : "}" #>o--<#= foreignKey.IsRequired ? "|" : "o" #>| <#= foreignKey.PrincipalEntityType.Name #> : "<#= foreignKey.GetConstraintName() #>"
<#
}
foreach (var skipNavigation in entityType.GetSkipNavigations().Where(n => n.IsLeftNavigation()))
{
#>
<#= entityType.Name #> }o--o{ <#= skipNavigation.TargetEntityType.Name #> : <#= skipNavigation.JoinEntityType.Name #>
<#
}
}
#>
```