
- 實體框架教程
- 實體框架 - 首頁
- 實體框架 - 概述
- 實體框架 - 架構
- 實體框架 - 環境設定
- 實體框架 - 資料庫設定
- 實體框架 - 資料模型
- 實體框架 - DbContext
- 實體框架 - 型別
- 實體框架 - 關係
- 實體框架 - 生命週期
- 實體框架 - 程式碼優先方法
- 實體框架 - 模型優先方法
- 實體框架 - 資料庫優先方法
- 實體框架 - 開發方法
- 實體框架 - 資料庫操作
- 實體框架 - 併發
- 實體框架 - 事務
- 實體框架 - 檢視
- 實體框架 - 索引
- 實體框架 - 儲存過程
- 實體框架 - 離線實體
- 實體框架 - 表值函式
- 實體框架 - 原生SQL
- 實體框架 - 列舉支援
- 實體框架 - 非同步查詢
- 實體框架 - 持久化
- 實體框架 - 投影查詢
- 實體框架 - 命令日誌
- 實體框架 - 命令攔截
- 實體框架 - 空間資料型別
- 實體框架 - 繼承
- 實體框架 - 遷移
- 實體框架 - 渴望載入
- 實體框架 - 延遲載入
- 實體框架 - 顯式載入
- 實體框架 - 驗證
- 實體框架 - 跟蹤更改
- 實體框架 - 彩色實體
- 實體框架 - 程式碼優先方法
- 實體框架 - 第一個示例
- 實體框架 - 資料註解
- 實體框架 - Fluent API
- 實體框架 - 種子資料庫
- 實體框架 - 程式碼優先遷移
- 實體框架 - 多個DbContext
- 實體框架 - 巢狀實體型別
- 實體框架資源
- 實體框架 - 快速指南
- 實體框架 - 有用資源
- 實體框架 - 討論
實體框架 - 資料註解
DataAnnotations 用於配置類,這些類將突出顯示最常用的配置。DataAnnotations 也被許多 .NET 應用程式(如 ASP.NET MVC)理解,這允許這些應用程式利用相同的註解進行客戶端驗證。DataAnnotation 屬性會覆蓋預設的 CodeFirst 約定。
System.ComponentModel.DataAnnotations 包括以下影響列的可空性或大小的屬性。
- Key
- Timestamp
- ConcurrencyCheck
- Required
- MinLength
- MaxLength
- StringLength
System.ComponentModel.DataAnnotations.Schema 名稱空間包含以下影響資料庫模式的屬性。
- Table
- Column
- Index
- ForeignKey
- NotMapped
- InverseProperty
Key
實體框架依賴於每個實體都具有一個鍵值,它用於跟蹤實體。Code First 依賴的約定之一是如何暗示每個 Code First 類中的哪個屬性是鍵。
約定是查詢名為“Id”的屬性或將類名和“Id”組合在一起的屬性,例如“StudentId”。
該屬性將對映到資料庫中的主鍵列。
Student、Course 和 Enrollment 類遵循此約定。
現在假設 Student 類使用 StdntID 而不是 ID。當 Code First 找不到與該約定匹配的屬性時,由於實體框架要求您必須具有鍵屬性,因此它將丟擲異常。您可以使用 key 註解來指定哪個屬性用作 EntityKey。
讓我們看一下包含 StdntID 的 Student 類的以下程式碼,但它不遵循預設的 Code First 約定。因此,為了處理這個問題,添加了一個 Key 屬性,這將使其成為主鍵。
public class Student { [Key] public int StdntID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
當您執行應用程式並在 SQL Server 資源管理器中檢視資料庫時,您將看到主鍵現在是 Students 表中的 StdntID。

實體框架也支援複合鍵。複合鍵也是主鍵,由多個屬性組成。例如,您有一個 DrivingLicense 類,其主鍵是 LicenseNumber 和 IssuingCountry 的組合。
public class DrivingLicense { [Key, Column(Order = 1)] public int LicenseNumber { get; set; } [Key, Column(Order = 2)] public string IssuingCountry { get; set; } public DateTime Issued { get; set; } public DateTime Expires { get; set; } }
當您有複合鍵時,實體框架要求您定義鍵屬性的順序。您可以使用 Column 註解來指定順序。

Timestamp
Code First 將以與 ConcurrencyCheck 屬性相同的方式處理 Timestamp 屬性,但它還將確保 Code First 生成的資料庫欄位不可為空。
更常見的是使用 rowversion 或 timestamp 欄位進行併發檢查。
您可以使用更具體的 TimeStamp 註解而不是使用 ConcurrencyCheck 註解,只要屬性的型別是位元組陣列即可。
您在一個給定類中只能有一個 timestamp 屬性。
讓我們看一個簡單的例子,將 TimeStamp 屬性新增到 Course 類中 -
public class Course { public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; } [Timestamp] public byte[] TStamp { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
如上例所示,Timestamp 屬性應用於 Course 類的 Byte[] 屬性。因此,Code First 將在 Courses 表中建立一個名為 TStamp 的 timestamp 列。
ConcurrencyCheck
ConcurrencyCheck 註解允許您標記一個或多個屬性,以便在使用者編輯或刪除實體時在資料庫中用於併發檢查。如果您一直在使用 EF 設計器,這與將屬性的 ConcurrencyMode 設定為 Fixed 相一致。
讓我們看一個 ConcurrencyCheck 如何工作的簡單示例,將其新增到 Course 類中的 Title 屬性中。
public class Course { public int CourseID { get; set; } [ConcurrencyCheck] public string Title { get; set; } public int Credits { get; set; } [Timestamp, DataType("timestamp")] public byte[] TimeStamp { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
在上面的 Course 類中,ConcurrencyCheck 屬性應用於現有的 Title 屬性。現在,Code First 將在更新命令中包含 Title 列以檢查樂觀併發,如下面的程式碼所示。
exec sp_executesql N'UPDATE [dbo].[Courses] SET [Title] = @0 WHERE (([CourseID] = @1) AND ([Title] = @2)) ',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus' go
Required 註解
Required 註解告訴 EF 特定屬性是必需的。讓我們看一下以下 Student 類,其中 Required id 新增到 FirstMidName 屬性中。Required 屬性將強制 EF 確保該屬性包含資料。
public class Student { [Key] public int StdntID { get; set; } [Required] public string LastName { get; set; } [Required] public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
如上例所示,Required 屬性應用於 FirstMidName 和 LastName。因此,Code First 將在 Students 表中建立 NOT NULL FirstMidName 和 LastName 列,如下面的影像所示。

MaxLength
MaxLength 屬性允許您指定其他屬性驗證。它可以應用於域類的字串或陣列型別屬性。EF Code First 將根據 MaxLength 屬性中指定的大小設定列的大小。
讓我們看一下以下 Course 類,其中 MaxLength(24) 屬性應用於 Title 屬性。
public class Course { public int CourseID { get; set; } [ConcurrencyCheck] [MaxLength(24)] public string Title { get; set; } public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
當您執行上述應用程式時,Code First 將在 CourseId 表中建立一個名為 Title 的 nvarchar(24) 列,如下面的影像所示。

當用戶設定包含超過 24 個字元的 Title 時,EF 將丟擲 EntityValidationError。
MinLength
MinLength 屬性還允許您指定其他屬性驗證,就像您使用 MaxLength 一樣。MinLength 屬性也可以與 MaxLength 屬性一起使用,如下面的程式碼所示。
public class Course { public int CourseID { get; set; } [ConcurrencyCheck] [MaxLength(24) , MinLength(5)] public string Title { get; set; } public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
如果將 Title 屬性的值設定為小於 MinLength 屬性中指定長度或大於 MaxLength 屬性中指定長度的值,則 EF 將丟擲 EntityValidationError。
StringLength
StringLength 也允許您指定其他屬性驗證,如 MaxLength。唯一的區別是 StringLength 屬性只能應用於域類的字串型別屬性。
public class Course { public int CourseID { get; set; } [StringLength (24)] public string Title { get; set; } public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
實體框架還會驗證 StringLength 屬性的屬性值。如果使用者設定包含超過 24 個字元的 Title,則 EF 將丟擲 EntityValidationError。
Table
預設的 Code First 約定建立與類名類似的表名。如果您讓 Code First 建立資料庫,並且還希望更改它正在建立的表名。然後 -
您可以將 Code First 與現有資料庫一起使用。但並非總是類名與資料庫中表名匹配的情況。
Table 屬性覆蓋此預設約定。
EF Code First 將為給定的域類建立一個具有 Table 屬性中指定名稱的表。
讓我們看一個以下示例,其中類名為 Student,並且根據約定,Code First 假設這將對映到名為 Students 的表。如果不是這種情況,您可以使用 Table 屬性指定表名,如下面的程式碼所示。
[Table("StudentsInfo")] public class Student { [Key] public int StdntID { get; set; } [Required] public string LastName { get; set; } [Required] public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
現在您可以看到 Table 屬性將表指定為 StudentsInfo。生成表時,您將看到表名 StudentsInfo,如下面的影像所示。

您不僅可以指定表名,還可以使用 Table 屬性指定表的架構,如下面的程式碼所示。
[Table("StudentsInfo", Schema = "Admin")] public class Student { [Key] public int StdntID { get; set; } [Required] public string LastName { get; set; } [Required] public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
您可以在上面的示例中看到,表使用 admin 架構指定。現在 Code First 將在 Admin 架構中建立 StudentsInfo 表,如下面的影像所示。

Column
它也與 Table 屬性相同,但 Table 屬性覆蓋錶行為,而 Column 屬性覆蓋列行為。預設的 Code First 約定建立與屬性名類似的列名。如果您讓 Code First 建立資料庫,並且還希望更改表中的列名。然後 -
Column 屬性覆蓋預設約定。
EF Code First 將為給定的屬性建立一個具有 Column 屬性中指定名稱的列。
讓我們看一個以下示例,其中屬性名為 FirstMidName,並且根據約定,Code First 假設這將對映到名為 FirstMidName 的列。
如果不是這種情況,您可以使用 Column 屬性指定列名,如下面的程式碼所示。
public class Student { public int ID { get; set; } public string LastName { get; set; } [Column("FirstName")] public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
您可以看到 Column 屬性將列指定為 FirstName。生成表時,您將看到列名 FirstName,如下面的影像所示。

Index
Index 屬性是在 Entity Framework 6.1 中引入的。如果您使用的是早期版本,則本節中的資訊不適用。
您可以使用 IndexAttribute 在一個或多個列上建立索引。
將屬性新增到一個或多個屬性將導致 EF 在建立資料庫時在資料庫中建立相應的索引。
在大多數情況下,索引可以使資料檢索更快更高效。但是,在表或檢視上使用過多的索引可能會對其他操作(如插入或更新)的效能產生負面影響。
索引是 Entity Framework 中的新功能,您可以透過減少從資料庫查詢資料所需的時間來提高 Code First 應用程式的效能。
您可以使用 Index 屬性向資料庫新增索引,並覆蓋預設的 Unique 和 Clustered 設定,以獲得最適合您方案的索引。
預設情況下,索引將命名為 IX_<屬性名稱>
讓我們看一下下面的程式碼,其中在 Course 類中為 Credits 添加了 Index 屬性。
public class Course { public int CourseID { get; set; } public string Title { get; set; } [Index] public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
您可以看到 Index 屬性已應用於 Credits 屬性。生成表時,您將在索引中看到 IX_Credits。

預設情況下,索引是非唯一的,但您可以使用IsUnique命名引數指定索引應該是唯一的。以下示例介紹了一個唯一的索引,如下面的程式碼所示。
public class Course { public int CourseID { get; set; } [Index(IsUnique = true)] public string Title { get; set; } [Index] public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
ForeignKey
Code First 約定將處理模型中最常見的關聯關係,但某些情況下需要幫助。例如,更改 Student 類中鍵屬性的名稱會導致與其關聯的 Enrollment 類出現問題。
public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } public Grade? Grade { get; set; } public virtual Course Course { get; set; } public virtual Student Student { get; set; } } public class Student { [Key] public int StdntID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
在生成資料庫時,Code First 會看到 Enrollment 類中的 StudentID 屬性,並根據約定(它與類名加“ID”匹配)將其識別為對 Student 類的外部索引鍵。但是,Student 類中沒有 StudentID 屬性,而是 StdntID 屬性在 Student 類中。
解決此問題的辦法是在 Enrollment 中建立一個導航屬性,並使用 ForeignKey 資料註釋來幫助 Code First 理解如何在兩個類之間構建關係,如下面的程式碼所示。
public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } public Grade? Grade { get; set; } public virtual Course Course { get; set; } [ForeignKey("StudentID")] public virtual Student Student { get; set; } }
您現在可以看到 ForeignKey 屬性已應用於導航屬性。

NotMapped
根據 Code First 的預設約定,每個支援的資料型別的屬性,並且包含 getter 和 setter 的屬性都將在資料庫中表示。但這並非始終適用於您的應用程式。NotMapped 屬性會覆蓋此預設約定。例如,您可能在 Student 類中有一個名為 FatherName 的屬性,但不需要儲存它。您可以將 NotMapped 屬性應用於 FatherName 屬性,您不希望在資料庫中建立該屬性的列,如下面的程式碼所示。
public class Student { [Key] public int StdntID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } [NotMapped] public int FatherName { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } }
您可以看到 NotMapped 屬性已應用於 FatherName 屬性。生成表時,您將看到資料庫中不會建立 FatherName 列,但它存在於 Student 類中。

對於沒有 getter 或 setter 的屬性,Code First 不會建立列,如下面的 Student 類 Address 和 Age 屬性示例所示。
InverseProperty
當您在類之間有多個關係時,使用 InverseProperty。在 Enrollment 類中,您可能希望跟蹤誰註冊了 Current Course 和 Previous Course。讓我們為 Enrollment 類新增兩個導航屬性。
public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } public Grade? Grade { get; set; } public virtual Course CurrCourse { get; set; } public virtual Course PrevCourse { get; set; } public virtual Student Student { get; set; } }
同樣,您還需要在這些屬性引用的 Course 類中新增。Course 類具有返回到 Enrollment 類的導航屬性,其中包含所有當前和以前的註冊資訊。
public class Course { public int CourseID { get; set; } public string Title { get; set; } [Index] public int Credits { get; set; } public virtual ICollection<Enrollment> CurrEnrollments { get; set; } public virtual ICollection<Enrollment> PrevEnrollments { get; set; } }
如果外部索引鍵屬性未包含在特定類中,則 Code First 會建立 {類名} _{主鍵} 外部索引鍵列,如上述類所示。生成資料庫時,您將看到以下外部索引鍵。

如您所見,Code First 無法自行匹配兩個類中的屬性。Enrollments 的資料庫表應該有一個用於 CurrCourse 的外部索引鍵和一個用於 PrevCourse 的外部索引鍵,但 Code First 將建立四個外部索引鍵屬性,即
- CurrCourse _CourseID
- PrevCourse _CourseID
- Course_CourseID 和
- Course_CourseID1
要解決這些問題,您可以使用 InverseProperty 註釋來指定屬性的對齊方式。
public class Course { public int CourseID { get; set; } public string Title { get; set; } [Index] public int Credits { get; set; } [InverseProperty("CurrCourse")] public virtual ICollection<Enrollment> CurrEnrollments { get; set; } [InverseProperty("PrevCourse")] public virtual ICollection<Enrollment> PrevEnrollments { get; set; } }
如您所見,透過指定它屬於 Enrollment 類的哪個引用屬性,在上面的 Course 類中應用了 InverseProperty 屬性。現在,Code First 將生成一個數據庫並在 Enrollments 表中僅建立兩個外部索引鍵列,如下面的影像所示。

我們建議您逐步執行以上示例,以便更好地理解。