.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號: N200421801
出刊日期: 2020/4/1
Entity Framework Core提供了兩套工具程式讓我們對資料庫進行操作,像是進行逆向工程(Reverse engineering),這兩套工具分別為:套件管理員主控台 (Package Manager Console) 命令(使用 NuGet Package Manager下載)與EF Core 命令列工具 (command-line interface (CLI))。習慣使用微軟開發工具的程式設計師,常常會問一個問題:「這些操作是否有圖型介面可以使用 ?」。「Entity Framework Core Power Tools」是你的最佳朋友。在這篇文章中,我們將介紹這個套件,除了提供視覺化的介面來進行逆向工程(Reverse engineering)之外,還提供了哪些好用的功能。
Entity Framework Core Power Tools安裝
首先你需要從Visual Studio 2019開發工具「延伸模組」-「管理延伸模組」選項開啟「管理擴充功能」對話盒,選取左方清單「線上」分類,然後在右上方文字方塊輸入「EF Core Power Tools」關鍵字搜尋,找到後按下「下載」按鈕,從網路下載下來安裝,請參考下圖所示:
圖 1:Entity Framework Core Power Tools安裝。
接著會要求關閉Visual Studio 開發工具,之後便開始進入安裝作業,點選畫面中的「Modify」按鈕,請參考下圖所示:
圖 2:進入安裝作業。
再來會開始安裝動作,直到安裝完成,請參考下圖所示:
圖 3:開始安裝。
從Visual Studio 2019開發工具「檔案」-「新增」-「專案」項目,在「建立新專案」對話盒中,第一個下拉式清單方塊選取「C#」程式語言;從第二個下拉式清單方塊選取「所有平台」;從第三個下拉式清單方塊選取「主控台」,然後選取下方的「主控台應用程式(.NET Core)」範本。請參考下圖所示:
圖 4:建立主控台應用程式。
在「設定新的專案」對話盒中,設定專案名稱與儲存位置,然後按下「建立」按鈕,請參考下圖所示:
圖 5:「設定新的專案」。
逆向工程(Reverse engineering)
若要進行Entity Framework Core逆向工程(Reverse engineering),從現有資料庫的結構描述資訊,來產生開發所需的實體類別程式碼,可以選擇Visual Studio 2019開發工具「方案總管」中的專案名稱,按一下滑鼠右鍵,從快捷選單中,選取「EF Core Power Tools」-「Reverse Engineer」選項,請參考下圖所示:
圖 6:逆向工程(Reverse engineering)。
下一步是連接到資料庫,目前支援多種資料庫,包含SQL Server、SQLite、Postgres、MySQL...等等。由於本範例是以「Entity Framework Core 3.1.x」版,需在「Choose Database Connection」對話盒,勾選「Use EF Core 3.0」核取方塊,然後按一下「Add」按鈕,請參考下圖所示:
圖 7:連接到資料庫。
我們以連接到微軟開發用的SQL Server Express 2019版為例,在「連接屬性」視窗中,設以下屬性,請參考下圖所示:
· 資料來源 (Data Source) :Microsoft SQL Server (SqlClient)。
· 伺服器名稱(Server name)欄位:輸入「.\SQLExpress」。
· 驗證(Authentication):選取「Windows驗證(Windows Authentication)」。
· 選取或輸入資料庫名稱(Select or enter a database name)欄位:選擇「Northwind」資料庫。
圖 8:連接到微軟開發用的SQL Server Express 2019版。
在「Select Tables to Script」對話盒,勾選要使用的資料表(可以選取多個),在此為簡單起見,本例只有選取一個「Region」資料表,然後按下「OK」按鈕,請參考下圖所示:
圖 9:勾選要使用的資料表(可以選取多個)。
參考下圖,在「Generate EF Core Model in Project EFPTDemo」對話盒設定以下內容:
圖 10:「Generate EF Core Model in Project EFPTDemo」對話盒。
按下「OK」鍵,就會根據上個步驟的設定,來產生程式碼。若沒有發生錯誤,完成後,便可以看到執行成功的訊息,請參考��圖所示:
圖 11:執行成功的訊息。
EF Core Power Tools會自動在專案之中,加入「Microsoft.EntityFrameworkCore.SqlServer」套件,並且自動在你指定的「Data」、「Models」資料夾之中產生「NorthwindContext.cs」以及「Region.cs」檔案,請參考下圖所示:
圖 12:自動安裝套件與產生實體類別程式碼。
其中「NorthwindContext.cs」檔案中包含的程式碼,定義一個「NorthwindContext」類別繼承自「DbContext」類別,負責跟實際的資料庫伺服器溝通,「NorthwindContext」類別中定義一個「Regions」屬性,對應到資料庫中「Region」資料表。因為在「Generate EF Core Model in Project EFPTDemo」對話盒之中勾選了「Include connection string in generated code」選項,因此「OnConfiguring」方法中包含程式碼設定了連接到資料庫的連接字串。「OnModelCreating」方法則包含程式碼設定資料表中的欄位資訊:
NorthwindContext.cs檔案程式碼列表
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using EFPTDemo.Models;
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace EFPTDemo.Data {
public partial class NorthwindContext : DbContext {
public NorthwindContext() {
}
public NorthwindContext( DbContextOptions<NorthwindContext> options )
: base( options ) {
}
public virtual DbSet<Region> Regions { get; set; }
protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder ) {
if ( !optionsBuilder.IsConfigured ) {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
optionsBuilder.UseSqlServer( "Data Source=.\\sqlexpress;Initial Catalog=Northwind;Integrated Security=True" );
}
}
protected override void OnModelCreating( ModelBuilder modelBuilder ) {
modelBuilder.Entity<Region>( entity => {
entity.HasKey( e => e.RegionId )
.IsClustered( false );
entity.Property( e => e.RegionId ).ValueGeneratedNever();
entity.Property( e => e.RegionDescription ).IsFixedLength();
} );
OnModelCreatingPartial( modelBuilder );
}
partial void OnModelCreatingPartial( ModelBuilder modelBuilder );
}
}
「Region.cs」檔案則定義了對應到資料表欄位的屬性,請參考以下程式碼列表:
Region.cs檔案程式碼列表
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EFPTDemo.Models {
[Table( "Region" )]
public partial class Region {
[Key]
[Column( "RegionID" )]
public int RegionId { get; set; }
[Required]
[StringLength( 50 )]
public string RegionDescription { get; set; }
}
}
專案中根資料夾下會額外產生一個JSON格式的「efpt.config.json」設定檔案,此檔案記錄了你在EF Power Tools之中所做的設定。
efpt.config.json檔案程式碼列表
{
"ContextClassName": "NorthwindContext",
"ContextNamespace": null,
"DefaultDacpacSchema": null,
"DoNotCombineNamespace": false,
"IdReplace": false,
"IncludeConnectionString": true,
"ModelNamespace": null,
"OutputContextPath": "Data",
"OutputPath": "Models",
"ProjectRootNamespace": "EFPTDemo",
"SelectedHandlebarsLanguage": 0,
"SelectedToBeGenerated": 0,
"Tables": [
{
"HasPrimaryKey": true,
"Name": "[dbo].[Region]"
}
],
"UseDatabaseNames": false,
"UseFluentApiOnly": false,
"UseHandleBars": false,
"UseInflector": true,
"UseLegacyPluralizer": false,
"UseSpatial": false
}
使用DbContext物件
實體類別與DbContext類別產生完之後,就可以利用這些類別來存取資料庫資料,修改「Program」類別程式碼,在「Main」方法中,利用Entity Framework Core查詢「Northwind」資料庫「Region」資料表中的所有資料,參考以下範例程式碼:
using EFPTDemo.Data;
using System;
namespace EFPTDemo {
class Program {
static void Main( string[] args ) {
using ( NorthwindContext context = new NorthwindContext() ) {
foreach ( var item in context.Regions ) {
Console.WriteLine($"Region Id : {item.RegionId} , Region Description : {item.RegionDescription}" );
}
}
}
}
}
這個範例程式的執行結果參考如下:
圖 13:查詢「Northwind」資料庫「Region」資料表中的所有資料。
加入Model Diagram
下一個要介紹的是加入Entity Framework Core Model Diagram的功能。若選擇Visual Studio 2019開發工具「方案總管」中的專案名稱,按一下滑鼠右鍵,從快捷選單中,選取「EF Core Power Tools」-「Add DbContext Model Diagram」選項,請參考下圖所示:
圖 14:加入Model Diagram。
接著會根據專案中的DbContext類別產生出一個副檔名為dbml的檔案,以視覺化的圖型來顯示模型中實體之間的關係與屬性。
圖 15:Model Diagram。
特別注意,Visual Studio 2019需要在安裝時,選擇「Individual components」項目,然後勾選要安裝「Architecture and analysis tools」,才會有視覺化圖型介面來呈現模型。
圖 16:安裝「Architecture and analysis tools」。
Dgml檔案是XML格式,以這個範例而言,產生的「NorthwindContext.dgml」檔案內容如下:
NorthwindContext.dgml檔案程式碼列表
<?xml version="1.0" encoding="utf-8"?>
<DirectedGraph GraphDirection="LeftToRight" xmlns="
http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="IModel" Category="Model" Annotations="Relational:MaxIdentifierLength: 128 SqlServer:ValueGenerationStrategy: IdentityColumn" Bounds="-1.4210854715202E-14,-2.8421709430404E-14,197.15,201.92" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" Label="NorthwindContext" ProductVersion="3.1.1" PropertyAccessMode="PropertyAccessMode.Default" UseManualLocation="True" />
<Node Id="Region" Category="EntityType" Annotations="" BaseClass="" Bounds="20,40,157.15,141.92" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="Region" Name="Region" />
<Node Id="Region.RegionDescription" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="MaxLength: 50 Relational:IsFixedLength: True TypeMapping: Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerStringTypeMapping" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="40,135.96,117.15,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="RegionDescription" MaxLength="50" Name="RegionDescription" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="Region.RegionId" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:ColumnName: RegionID TypeMapping: Microsoft.EntityFrameworkCore.Storage.IntTypeMapping" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="40,80,67.1566666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="RegionId" MaxLength="None" Name="RegionId" PropertyAccessMode="PropertyAccessMode.Default" Type="int" ValueGenerated="None" />
</Nodes>
<Links>
<Link Source="IModel" Target="Region" Category="Contains" />
<Link Source="Region" Target="Region.RegionDescription" Category="Contains" />
<Link Source="Region" Target="Region.RegionId" Category="Contains" />
</Links>
<Categories>
<Category Id="Contains" Label="包含" Description="連結的來源是否包含目標物件" CanBeDataDriven="False" CanLinkedNodesBeDataDriven="True" IncomingActionLabel="由下列包含" IsContainment="True" OutgoingActionLabel="包含" />
<Category Id="EntityType" />
<Category Id="Model" />
<Category Id="Property Primary" />
<Category Id="Property Required" />
</Categories>
<Properties>
<Property Id="AfterSaveBehavior" Group="Property Flags" DataType="System.String" />
<Property Id="Annotations" Description="Annotations" Group="Model Properties" DataType="System.String" />
<Property Id="BaseClass" Description="Base class" Group="Model Properties" DataType="System.String" />
<Property Id="BeforeSaveBehavior" Group="Property Flags" DataType="System.String" />
<Property Id="Bounds" DataType="System.Windows.Rect" />
<Property Id="CanBeDataDriven" Label="CanBeDataDriven" Description="CanBeDataDriven" DataType="System.Boolean" />
<Property Id="CanLinkedNodesBeDataDriven" Label="CanLinkedNodesBeDataDriven" Description="CanLinkedNodesBeDataDriven" DataType="System.Boolean" />
<Property Id="ChangeTrackingStrategy" Description="Change tracking strategy" Group="Model Properties" DataType="System.String" />
<Property Id="Expression" DataType="System.String" />
<Property Id="Field" Description="Backing field" Group="Model Properties" DataType="System.String" />
<Property Id="GraphDirection" DataType="Microsoft.VisualStudio.Diagrams.Layout.LayoutOrientation" />
<Property Id="Group" Label="群組" Description="將節點顯示為群組" DataType="Microsoft.VisualStudio.GraphModel.GraphGroupStyle" />
<Property Id="GroupLabel" DataType="System.String" />
<Property Id="IncomingActionLabel" Label="IncomingActionLabel" Description="IncomingActionLabel" DataType="System.String" />
<Property Id="IsAbstract" Label="IsAbstract" Description="IsAbstract" Group="Model Properties" DataType="System.Boolean" />
<Property Id="IsAlternateKey" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsConcurrencyToken" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsContainment" DataType="System.Boolean" />
<Property Id="IsEnabled" DataType="System.Boolean" />
<Property Id="IsForeignKey" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsIndexed" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsPrimaryKey" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsRequired" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsShadow" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsUnicode" Group="Property Flags" DataType="System.Boolean" />
<Property Id="Label" Label="標籤" Description="可註釋物件的可顯示標籤" DataType="System.String" />
<Property Id="MaxLength" DataType="System.String" />
<Property Id="Name" Group="Model Properties" DataType="System.String" />
<Property Id="OutgoingActionLabel" Label="OutgoingActionLabel" Description="OutgoingActionLabel" DataType="System.String" />
<Property Id="ProductVersion" Label="Product Version" Description="EF Core product version" Group="Model Properties" DataType="System.String" />
<Property Id="PropertyAccessMode" Group="Property Flags" DataType="System.String" />
<Property Id="TargetType" DataType="System.Type" />
<Property Id="Type" Description="CLR data type" Group="Model Properties" DataType="System.String" />
<Property Id="UseManualLocation" DataType="System.Boolean" />
<Property Id="Value" DataType="System.String" />
<Property Id="ValueGenerated" Group="Property Flags" DataType="System.String" />
<Property Id="ValueLabel" DataType="System.String" />
</Properties>
<Styles>
<Style TargetType="Node" GroupLabel="EntityType" ValueLabel="True">
<Condition Expression="HasCategory('EntityType')" />
<Setter Property="Background" Value="#FFC0C0C0" />
</Style>
<Style TargetType="Node" GroupLabel="Property Primary" ValueLabel="True">
<Condition Expression="HasCategory('Property Primary')" />
<Setter Property="Background" Value="#FF008000" />
</Style>
<Style TargetType="Node" GroupLabel="Property Optional" ValueLabel="True">
<Condition Expression="HasCategory('Property Optional')" />
<Setter Property="Background" Value="#FF808040" />
</Style>
<Style TargetType="Node" GroupLabel="Property Foreign" ValueLabel="True">
<Condition Expression="HasCategory('Property Foreign')" />
<Setter Property="Background" Value="#FF8080FF" />
</Style>
<Style TargetType="Node" GroupLabel="Property Required" ValueLabel="True">
<Condition Expression="HasCategory('Property Required')" />
<Setter Property="Background" Value="#FFC0A000" />
</Style>
<Style TargetType="Node" GroupLabel="Navigation Property" ValueLabel="True">
<Condition Expression="HasCategory('Navigation Property')" />
<Setter Property="Background" Value="#FF990000" />
</Style>
<Style TargetType="Node" GroupLabel="Navigation Collection" ValueLabel="True">
<Condition Expression="HasCategory('Navigation Collection')" />
<Setter Property="Background" Value="#FFFF3232" />
</Style>
<Style TargetType="Node" GroupLabel="Model" ValueLabel="True">
<Condition Expression="HasCategory('Model')" />
<Setter Property="Background" Value="#FFFFFFFF" />
</Style>
</Styles>
</DirectedGraph>
View DbContext Model DDL SQL
若選擇Visual Studio 2019開發工具「方案總管」中的專案名稱,按一下滑鼠右鍵,從快捷選單中,選取「EF Core Power Tools」-「View DbContext Model DDL SQL」選項,請參考下圖所示:
圖 17:View DbContext Model DDL SQL。
接著在專案中便會根據目前DbContext模型來產生一個SQL檔案,描述要建立的資料庫結構,以本例來說,產生以下CREATE語法程式碼:
CREATE TABLE [Region] (
[RegionID] int NOT NULL,
[RegionDescription] nchar(50) NOT NULL,
CONSTRAINT [PK_Region] PRIMARY KEY NONCLUSTERED ([RegionID])
);
GO
「View DbContext Model DDL SQL」功能執行結果,請參考下圖所示:
圖 18:「View DbContext Model DDL SQL」功能執行結果。
View DbContext Model as DebugView
若選擇Visual Studio 2019開發工具「方案總管」中的專案名稱,按一下滑鼠右鍵,從快捷選單中,選取「EF Core Power Tools」-「View DbContext Model as DebugView」選項,請參考下圖所示:
圖 19:「View DbContext Model as DebugView」選項。
將會產生一個文字檔顯示在編輯畫面,其中描述模型的Metadata,以方便程式設計師來了解模型,以及幫助除錯。請參考以下檔案內容的列表:
Model:
EntityType: Region
Properties:
RegionId (int) Required PK AfterSave:Throw
Annotations:
Relational:ColumnName: RegionID
TypeMapping: Microsoft.EntityFrameworkCore.Storage.IntTypeMapping
RegionDescription (string) Required MaxLength50
Annotations:
MaxLength: 50
Relational:IsFixedLength: True
TypeMapping: Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerStringTypeMapping
Keys:
RegionId PK
Annotations:
SqlServer:Clustered: False
Annotations:
ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding
Relational:TableName: Region
Annotations:
ProductVersion: 3.1.1
Relational:MaxIdentifierLength: 128
SqlServer:ValueGenerationStrategy: IdentityColumn
在使用Visual Studio 工具除錯時,也可以在中斷模式,從除錯視窗檢視這些資訊,請參考下圖所示,展開「context」-「Model」-「DebugView」-「View」選項:
圖 20:除錯視窗。
點選放大鏡圖示就會開啟「文字視覺化檢視」視窗,請參考下圖所示:
圖 21:顯示模型資訊。
Add AsDgml() extension method
若選擇Visual Studio 2019開發工具「方案總管」中的專案名稱,按一下滑鼠右鍵,從快捷選單中,選取「EF Core Power Tools」-「Add AsDgml() extension method」選項,請參考下圖所示:
圖 22:「Add AsDgml() extension method」選項。
選擇「Add AsDgml() extension method」選項會自動在專案中安裝一個「ErikEJ.EntityFrameworkCore.DgmlBuilder」套件,可為DbContext類別新增一個「AsDgml()」擴充方法,同時開發工具會顯示一個暫存的文字檔案,其中包含以下讀我內容,提供參考範例程式碼來產生dbml檔案:
** ErikEJ.EntityFrameworkCore.DgmlBuilder Readme **
To use the extension method to generate a DGML file of your DbContext model,
use code similar to this:
using Microsoft.EntityFrameworkCore;
using (var myContext = new MyDbContext())
{
System.IO.File.WriteAllText(System.IO.Path.GetTempFileName() + ".dgml",
myContext.AsDgml(),
System.Text.Encoding.UTF8);
}
讓我們修改主控台應用程式的「Main」方法如下:
using EFPTDemo.Data;
using Microsoft.EntityFrameworkCore;
using System;
namespace EFPTDemo {
class Program {
static void Main( string[] args ) {
using ( var myContext = new NorthwindContext() ) {
string file = System.IO.Path.GetTempFileName() + ".dgml";
Console.WriteLine(file); //C:\Users\UserName\AppData\Local\Temp\tmp2CAF.tmp.dgml
System.IO.File.WriteAllText( file , myContext.AsDgml() ,System.Text.Encoding.UTF8 );
}
}
}
}
執行程式之後,就會在指定的資料夾產生dbml檔案。
View Database Schema as Graph
若選擇Visual Studio 2019開發工具「方案總管」中的專案名稱,按一下滑鼠右鍵,從快捷選單中,選取「EF Core Power Tools」-「View Database Schema as Graph」選項,請參考下圖所示:
圖 23:「View Database Schema as Graph」選項。
下一步是連接到資料庫,由於本範例是以「Entity Framework Core 3.1.x」版,需在「Choose Database Connection」對話盒,勾選「Use EF Core 3.0」核取方塊,然後按一下「Add」按鈕,請參考下圖所示:
圖 24:連接到資料庫。
在「Select Tables to Script」對話盒,勾選要使用的資料表(可以選取多個),在此選取「Categories」與「Products」資料表,然後按下「OK」按鈕,請參考下圖所示:
圖 25:勾選要使用的資料表。
接下來就可以看到Model Diagram,請參考下圖所示,點選向下的箭頭可以展開群組資訊:
圖 26:Model Diagram。
接著在圖型介面中,便可以看到更詳細的資料表欄位資訊,請參考下圖所示:
圖 27:資料表欄位資訊。