ASP.Net MVC數據註解
DataAnnotations用於配置模型類,它將突出顯示最常用的配置。 DataAnnotations也被許多.NET應用程序所理解,例如ASP.NET MVC,它允許這些應用程序利用相同的註釋來進行客戶端驗證。DataAnnotation屬性重寫默認的Code-First約定。
System.ComponentModel.DataAnnotations包括以下影響列的可空性或大小的屬性。
- Key
- Timestamp
- ConcurrencyCheck
- Required
- MinLength
- MaxLength
- StringLength
System.ComponentModel.DataAnnotations.Schema命名空間包括以下影響數據庫模式的屬性。
- Table
- Column
- Index
- ForeignKey
- NotMapped
- InverseProperty
1. 鍵(Key)
實體框架(Entity Framework或簡稱爲EF )依賴於具有用於跟蹤實體的鍵值的每個實體。 Code First依賴的其中一個約定是它如何暗示哪個屬性是每個Code First類中的鍵。
約定是尋找一個名爲Id的屬性,或者將類名和Id結合起來的屬性,比如StudentId。 該屬性將映射到數據庫中的主鍵列。學生,課程和入學課程遵循這個約定。
現在讓假設Student類使用名稱StdntID而不是ID。 當Code First找不到符合此約定的屬性時,它將拋出一個異常,因爲Entity Framework要求必須具有一個鍵屬性。
可以使用鍵註釋來指定哪個屬性將被用作EntityKey。
下面來看看包含StdntID的Student類。 它不遵循默認的Code First約定,所以要處理這個問題,添加了Key屬性,使StdntID列成爲主鍵。
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。
實體框架(Entity Framework)也支持複合鍵。 複合鍵是由多個屬性組成的主鍵。例如,有一個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註釋來指定順序。
2. 時間戳(Timestamp)
Code First會將Timestamp屬性視爲ConcurrencyCheck屬性,但它也將確保Code First生成的數據庫字段不可空。
使用rowversion或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。
3. ConcurrencyCheck
ConcurrencyCheck註釋允許在用戶編輯或刪除實體時標記一個或多個要用於數據庫併發檢查的屬性。如果一直在使用EF Designer,那麼這將與將屬性的「併發性模式」設置爲「固定」一致。
下面來看看一個簡單的例子,通過將它添加Title屬性到Course類中來了解ConcurrencyCheck是如何工作的。
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將在update命令中包含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
4. Required
Required註釋告訴實體框架(Entity Framework)需要一個特定的屬性。下面來看看Student類,其中必需的id被添加到FirstMidName屬性中。 Required屬性將強制Entity Framework 確保該屬性中包含數據。
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; }
}
可以在上面的Student類的示例中看到Required屬性應用於FirstMidName和LastName。 因此,Code First將在Students表中創建一個NOT NULL的 FirstMidName和LastName列,如下圖所示。
5. MaxLength
MaxLength屬性用於指定其他屬性驗證。它可以應用於類的字符串或數組類型的屬性。 Entity Framework的 Code First 將設置MaxLength屬性中指定的列的大小。
下面來看看MaxLength(24)屬性應用於Title屬性的以下Course類。
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將在Coursed表中創建一個nvarchar(24)列標題,如以下屏幕截圖所示。
現在當用戶設置包含超過24個字符的標題時,Entity Framework將拋出EntityValidationError異常。
6. 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; }
}
如果在MinLength屬性中將Title屬性的值設置爲小於指定的長度或大於MaxLength屬性中的指定長度,則EF將拋出EntityValidationError異常。
7. StringLength
StringLength還允許指定其他屬性驗證,如MaxLength。 不同的是StringLength屬性只能應用於Domain類的字符串類型屬性。參考以下示例代碼 -
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; }
}
Entity Framework還驗證StringLength屬性的屬性值。 現在,如果用戶設置標題(Title),其中包含超過24個字符,那麼EF將拋出EntityValidationError異常。
8. Table
默認代碼第一個約定創建一個與類名相同的表名。 如果讓Code First創建數據庫,則還可以更改正在創建的表的名稱。可以讓Code First使用現有的數據庫表。 但並不總是這樣,有時類的名稱與數據庫中表的名稱不能總是相匹配。
Table屬性重寫此默認約定。 對於給定的域類,EF Code First將在Table屬性使用指定的名稱來創建一個表。
下面來看看一個類名爲Student的例子,按照慣例,Code First假定這將映射到一個名爲Students的表。 如果不是這種情況,可以使用Table屬性指定表的名稱,如以下代碼所示 指定要創建的表名稱爲:StudentInfo -
[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。 生成表時,如下圖所示的表名StudentInfo。
不僅可以指定表名,還可以使用以下代碼使用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表,如以下屏幕截圖所示。
9. Column
Column屬性也與Table屬性相同,但Table屬性覆蓋錶行爲,而Column屬性覆蓋列行爲。 默認代碼第一個約定創建一個與屬性名相同的列名。
如果讓Code First創建數據庫,並且還希望更改表中列的名稱。Column屬性重寫此默認約定。 EF Code First將在給定類屬性的Column屬性中創建一個具有指定名稱的列。
下面來看看下面的例子,其中屬性名爲FirstMidName,按照慣例,Code First假定這將映射到一個名爲FirstMidName的列。 如果不是要映射到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,如以下屏幕截圖所示。
10. Index
Index屬性是在Entity Framework 6.1中引入的。
注 - 如果您使用的是早期版本,則本節中的信息不適用。
可以使用Index屬性在一列或多列上創建索引。將屬性添加到一個或多個屬性將導致EF在創建數據庫時在數據庫中創建相應的索引。
在大多數情況下,索引使數據的檢索更快,更高效。但是,使用索引重載表或視圖可能會不愉快地影響其他操作(如插入或更新)的性能。
索引是實體框架中的新功能,可以通過減少從數據庫查詢數據所需的時間來提高Code First應用程序的性能。
可以使用Index屬性將索引添加到數據庫,並覆蓋默認的「唯一」和「羣集」設置,以獲得最適合您的方案的索引。默認情況下,索引將被命名爲IX_<屬性名稱>
讓我們來看看以下代碼,其中Index屬性被添加到Course類Credits列上。
public class Cours{
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; }
}
11. ForeignKey
Code First約定將處理模型中最常見的關係。 例如,通過更改Student類中的key屬性名稱,創建了與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屬性,而是Student類中的StdntID屬性。
解決方法是在Enrollment中創建導航屬性,並使用ForeignKey DataAnnotation來幫助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屬性應用於導航屬性。
12. 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類中。
Code First 不會爲沒有getter或setter的屬性創建一個列。
13. InverseProperty
在類之間有多個關係時使用InverseProperty。 在Enrollment類中,可能想要跟蹤註冊「當前課程」的人員和註冊「以前課程」的人員。
我們爲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會創建{Class Name} _ {Primary Key}外鍵列。 生成數據庫表時,您將看到許多外鍵,如以下屏幕截圖所示。
正如所看到的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; }
}
正如上面所看到的那樣,當InverseProperty屬性通過指定它所屬的Enrollment類的哪個引用屬性應用於上述Course類時,Code First將生成數據庫表,並在Enrollments表中創建兩個外鍵列,如以下屏幕截圖所示。
我們建議執行上述示例以便更好地理解。