鍍金池/ 教程/ C#/ ASP.Net MVC數(shù)據(jù)注解
ASP.Net MVC簡(jiǎn)介
ASP.Net MVC過(guò)濾器
ASP.Net MVC視圖
ASP.Net MVC安全
ASP.Net MVC手腳架
ASP.Net MVC控制器
ASP.Net MVC與SQL Server數(shù)據(jù)庫(kù)操作
ASP.Net MVC NuGet包管理
ASP.Net MVC入門程序
ASP.Net MVC Razor
ASP.Net MVC Bootstrap
ASP.Net MVC單元測(cè)試
ASP.Net MVC動(dòng)作
ASP.Net MVC模式
ASP.Net MVC選擇器
ASP.Net MVC開(kāi)發(fā)環(huán)境配置
ASP.Net MVC生命周期
ASP.Net MVC模型綁定
ASP.Net MVC自托管(本地主機(jī)部署)
ASP.Net MVC驗(yàn)證
ASP.Net MVC緩存
ASP.Net MVC數(shù)據(jù)模型
ASP.Net MVC路由
ASP.Net MVC教程
ASP.Net MVC助手
ASP.Net MVC數(shù)據(jù)注解
ASP.Net MVC Web API

ASP.Net MVC數(shù)據(jù)注解

DataAnnotations用于配置模型類,它將突出顯示最常用的配置。 DataAnnotations也被許多.NET應(yīng)用程序所理解,例如ASP.NET MVC,它允許這些應(yīng)用程序利用相同的注釋來(lái)進(jìn)行客戶端驗(yàn)證。DataAnnotation屬性重寫默認(rèn)的Code-First約定。

System.ComponentModel.DataAnnotations包括以下影響列的可空性或大小的屬性。

  • Key
  • Timestamp
  • ConcurrencyCheck
  • Required
  • MinLength
  • MaxLength
  • StringLength

System.ComponentModel.DataAnnotations.Schema命名空間包括以下影響數(shù)據(jù)庫(kù)模式的屬性。

  • Table
  • Column
  • Index
  • ForeignKey
  • NotMapped
  • InverseProperty

1. 鍵(Key)

實(shí)體框架(Entity Framework或簡(jiǎn)稱為EF )依賴于具有用于跟蹤實(shí)體的鍵值的每個(gè)實(shí)體。 Code First依賴的其中一個(gè)約定是它如何暗示哪個(gè)屬性是每個(gè)Code First類中的鍵。

約定是尋找一個(gè)名為Id的屬性,或者將類名和Id結(jié)合起來(lái)的屬性,比如StudentId。 該屬性將映射到數(shù)據(jù)庫(kù)中的主鍵列。學(xué)生,課程和入學(xué)課程遵循這個(gè)約定。

現(xiàn)在讓假設(shè)Student類使用名稱StdntID而不是ID。 當(dāng)Code First找不到符合此約定的屬性時(shí),它將拋出一個(gè)異常,因?yàn)?em>Entity Framework要求必須具有一個(gè)鍵屬性。

可以使用鍵注釋來(lái)指定哪個(gè)屬性將被用作EntityKey

下面來(lái)看看包含StdntIDStudent類。 它不遵循默認(rèn)的Code First約定,所以要處理這個(gè)問(wèn)題,添加了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; }
}

在運(yùn)行應(yīng)用程序并查看SQL Server資源管理器中的數(shù)據(jù)庫(kù)時(shí),您將看到現(xiàn)在Students表中的主鍵是:StdntID

實(shí)體框架(Entity Framework)也支持復(fù)合鍵。 復(fù)合鍵是由多個(gè)屬性組成的主鍵。例如,有一個(gè)DrivingLicense類,其主鍵是LicenseNumberIssuingCountry的組合。

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; }
}

當(dāng)有組合鍵時(shí),實(shí)體框架要求你定義鍵屬性的順序。可以使用Column注釋來(lái)指定順序。

2. 時(shí)間戳(Timestamp)

Code First會(huì)將Timestamp屬性視為ConcurrencyCheck屬性,但它也將確保Code First生成的數(shù)據(jù)庫(kù)字段不可空。

使用rowversiontimestamp字段進(jìn)行并發(fā)檢查更為常見(jiàn)。但是,不要使用ConcurrencyCheck注釋,只要屬性的類型是字節(jié)數(shù)組,就可以使用更具體的TimeStamp注釋。在給定的類中只能有一個(gè)時(shí)間戳屬性。

下面來(lái)看一個(gè)簡(jiǎn)單的例子,將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屬性應(yīng)用于Course類的Byte []屬性。 所以,Code First 將在Courses表中創(chuàng)建一個(gè)時(shí)間戳列TStamp

3. ConcurrencyCheck

ConcurrencyCheck注釋允許在用戶編輯或刪除實(shí)體時(shí)標(biāo)記一個(gè)或多個(gè)要用于數(shù)據(jù)庫(kù)并發(fā)檢查的屬性。如果一直在使用EF Designer,那么這將與將屬性的“并發(fā)性模式”設(shè)置為“固定”一致。

下面來(lái)看看一個(gè)簡(jiǎn)單的例子,通過(guò)將它添加Title屬性到Course類中來(lái)了解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屬性應(yīng)用于現(xiàn)有的Title屬性。 Code First將在update命令中包含Title列來(lái)檢查以下代碼所示的樂(lè)觀并發(fā)。

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注釋告訴實(shí)體框架(Entity Framework)需要一個(gè)特定的屬性。下面來(lái)看看Student類,其中必需的id被添加到FirstMidName屬性中。 Required屬性將強(qiáng)制Entity Framework 確保該屬性中包含數(shù)據(jù)。

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屬性應(yīng)用于FirstMidNameLastName。 因此,Code First將在Students表中創(chuàng)建一個(gè)NOT NULLFirstMidNameLastName列,如下圖所示。

5. MaxLength

MaxLength屬性用于指定其他屬性驗(yàn)證。它可以應(yīng)用于類的字符串或數(shù)組類型的屬性。 Entity FrameworkCode First 將設(shè)置MaxLength屬性中指定的列的大小。

下面來(lái)看看MaxLength(24)屬性應(yīng)用于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; }
}

當(dāng)運(yùn)行上述應(yīng)用程序時(shí),Code-First將在Coursed表中創(chuàng)建一個(gè)nvarchar(24)列標(biāo)題,如以下屏幕截圖所示。

現(xiàn)在當(dāng)用戶設(shè)置包含超過(guò)24個(gè)字符的標(biāo)題時(shí),Entity Framework將拋出EntityValidationError異常。

6. MinLength

MinLength屬性可指定其他屬性驗(yàn)證,就像上面使用的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屬性的值設(shè)置為小于指定的長(zhǎng)度或大于MaxLength屬性中的指定長(zhǎng)度,則EF將拋出EntityValidationError異常。

7. StringLength

StringLength還允許指定其他屬性驗(yàn)證,如MaxLength。 不同的是StringLength屬性只能應(yīng)用于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還驗(yàn)證StringLength屬性的屬性值。 現(xiàn)在,如果用戶設(shè)置標(biāo)題(Title),其中包含超過(guò)24個(gè)字符,那么EF將拋出EntityValidationError異常。

8. Table

默認(rèn)代碼第一個(gè)約定創(chuàng)建一個(gè)與類名相同的表名。 如果讓Code First創(chuàng)建數(shù)據(jù)庫(kù),則還可以更改正在創(chuàng)建的表的名稱??梢宰?em>Code First使用現(xiàn)有的數(shù)據(jù)庫(kù)表。 但并不總是這樣,有時(shí)類的名稱與數(shù)據(jù)庫(kù)中表的名稱不能總是相匹配。

Table屬性重寫此默認(rèn)約定。 對(duì)于給定的域類,EF Code First將在Table屬性使用指定的名稱來(lái)創(chuàng)建一個(gè)表。

下面來(lái)看看一個(gè)類名為Student的例子,按照慣例,Code First假定這將映射到一個(gè)名為Students的表。 如果不是這種情況,可以使用Table屬性指定表的名稱,如以下代碼所示 指定要?jiǎng)?chuàng)建的表名稱為: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; }
}

現(xiàn)在可以看到Table屬性將表指定為StudentsInfo。 生成表時(shí),如下圖所示的表名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模式。 現(xiàn)在,Code First將在Admin模式中創(chuàng)建StudentsInfo表,如以下屏幕截圖所示。

9. Column

Column屬性也與Table屬性相同,但Table屬性覆蓋表行為,而Column屬性覆蓋列行為。 默認(rèn)代碼第一個(gè)約定創(chuàng)建一個(gè)與屬性名相同的列名。

如果讓Code First創(chuàng)建數(shù)據(jù)庫(kù),并且還希望更改表中列的名稱。Column屬性重寫此默認(rèn)約定。 EF Code First將在給定類屬性的Column屬性中創(chuàng)建一個(gè)具有指定名稱的列。

下面來(lái)看看下面的例子,其中屬性名為FirstMidName,按照慣例,Code First假定這將映射到一個(gè)名為FirstMidName的列。 如果不是要映射到FirstMidName列時(shí),可以使用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; }
}

現(xiàn)在可以看到Column屬性將列指定為FirstName。 生成表后,可以看到列名為FirstName,如以下屏幕截圖所示。

10. Index

Index屬性是在Entity Framework 6.1中引入的。

注 - 如果您使用的是早期版本,則本節(jié)中的信息不適用。

可以使用Index屬性在一列或多列上創(chuàng)建索引。將屬性添加到一個(gè)或多個(gè)屬性將導(dǎo)致EF在創(chuàng)建數(shù)據(jù)庫(kù)時(shí)在數(shù)據(jù)庫(kù)中創(chuàng)建相應(yīng)的索引。

在大多數(shù)情況下,索引使數(shù)據(jù)的檢索更快,更高效。但是,使用索引重載表或視圖可能會(huì)不愉快地影響其他操作(如插入或更新)的性能。

索引是實(shí)體框架中的新功能,可以通過(guò)減少?gòu)臄?shù)據(jù)庫(kù)查詢數(shù)據(jù)所需的時(shí)間來(lái)提高Code First應(yīng)用程序的性能。

可以使用Index屬性將索引添加到數(shù)據(jù)庫(kù),并覆蓋默認(rèn)的“唯一”和“群集”設(shè)置,以獲得最適合您的方案的索引。默認(rèn)情況下,索引將被命名為IX_<屬性名稱>

讓我們來(lái)看看以下代碼,其中Index屬性被添加到CourseCredits列上。

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屬性應(yīng)用于Credits屬性。 現(xiàn)在,當(dāng)表生成時(shí),將在索引中看到名稱為IX_Credits的索引。

默認(rèn)情況下,索引是非唯一的,但是可以使用IsUnique命名參數(shù)來(lái)指定索引應(yīng)該是唯一的。 以下示例引入了一個(gè)唯一索引,如下面的代碼所示。

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約定將處理模型中最常見(jiàn)的關(guān)系。 例如,通過(guò)更改Student類中的key屬性名稱,創(chuàng)建了與Enrollment類的關(guān)系問(wèn)題。

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; }
}

在生成數(shù)據(jù)庫(kù)時(shí),Code First會(huì)在Enrollment類中看到StudentID屬性,并按照約定將其識(shí)別為類名稱加ID,作為Student類的外鍵。但是Student類中沒(méi)有StudentID屬性,而是Student類中的StdntID屬性。

解決方法是在Enrollment中創(chuàng)建導(dǎo)航屬性,并使用ForeignKey DataAnnotation來(lái)幫助Code First了解如何構(gòu)建兩個(gè)類之間的關(guān)系,如以下代碼所示。

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; }
}

現(xiàn)在可以看到ForeignKey屬性應(yīng)用于導(dǎo)航屬性。

12. NotMapped

Code First的約定在默認(rèn)情況下,每個(gè)屬于受支持?jǐn)?shù)據(jù)類型的屬性都包含gettersetter,它們?cè)跀?shù)據(jù)庫(kù)中表示。 但是在應(yīng)用中并不總是這樣。 NotMapped屬性將覆蓋此默認(rèn)約定。 例如,可能在Student類中有一個(gè)屬性,例如FatherName,但不需要存儲(chǔ)它到數(shù)據(jù)庫(kù)表。 那么可以將NotMapped屬性應(yīng)用于您不希望在數(shù)據(jù)庫(kù)中創(chuàng)建列的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屬性應(yīng)用于FatherName屬性。 現(xiàn)在,當(dāng)生成表時(shí),將看到FatherName列不會(huì)在數(shù)據(jù)庫(kù)中創(chuàng)建,但它存在于Student類中。

Code First 不會(huì)為沒(méi)有gettersetter的屬性創(chuàng)建一個(gè)列。

13. InverseProperty

在類之間有多個(gè)關(guān)系時(shí)使用InverseProperty。 在Enrollment類中,可能想要跟蹤注冊(cè)“當(dāng)前課程”的人員和注冊(cè)“以前課程”的人員。

我們?yōu)?code>Enrollment類添加兩個(gè)導(dǎo)航屬性。

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類的導(dǎo)航屬性返回到Enrollment類,其中包含當(dāng)前和以前的所有注冊(cè)。

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會(huì)創(chuàng)建{Class Name} _ {Primary Key}外鍵列。 生成數(shù)據(jù)庫(kù)表時(shí),您將看到許多外鍵,如以下屏幕截圖所示。

正如所看到的Code First 無(wú)法自己匹配兩個(gè)類的屬性。 用于Enrollments的數(shù)據(jù)庫(kù)表應(yīng)該有一個(gè)用于CurrCourse的外鍵和一個(gè)用于PrevCourse的外鍵,但Code First 將創(chuàng)建四個(gè)外鍵屬性,即 -

  • CurrCourse_CourseID
  • PrevCourse_CourseID
  • Course_CourseID
  • Course_CourseID1

要解決這些問(wèn)題,可以使用InverseProperty注解來(lái)指定屬性的對(duì)齊方式。

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; }
}

正如上面所看到的那樣,當(dāng)InverseProperty屬性通過(guò)指定它所屬的Enrollment類的哪個(gè)引用屬性應(yīng)用于上述Course類時(shí),Code First將生成數(shù)據(jù)庫(kù)表,并在Enrollments表中創(chuàng)建兩個(gè)外鍵列,如以下屏幕截圖所示。

我們建議執(zhí)行上述示例以便更好地理解。