自訂反向工程範本

注意

這項功能已在 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

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

進階使用方式

忽略輸入模型

ModelEntityType 參數代表與資料庫對應的其中一種可能方式。 您可以選擇忽略或變更模型的元件。 例如,我們提供的導覽名稱可能不理想,而且您可以在建構程式碼時將其取代為您自己的名稱。 其他像是條件約束名稱和索引篩選等專案只會由移轉使用,如果您不想要搭配 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 #>
<#
        }
    }
#>
```