Khung thực thể - Chú thích dữ liệu

DataAnnotations được sử dụng để định cấu hình các lớp sẽ làm nổi bật các cấu hình cần thiết nhất. DataAnnotations cũng được hiểu bởi một số ứng dụng .NET, chẳng hạn như ASP.NET MVC cho phép các ứng dụng này tận dụng các chú thích giống nhau để xác nhận phía máy khách. Thuộc tính DataAnnotation ghi đè các quy ước CodeFirst mặc định.

System.ComponentModel.DataAnnotations bao gồm các thuộc tính sau đây ảnh hưởng đến khả năng vô hiệu hoặc kích thước của cột.

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

System.ComponentModel.DataAnnotations.Schema không gian tên bao gồm các thuộc tính sau đây tác động đến lược đồ của cơ sở dữ liệu.

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

Chìa khóa

Khung thực thể dựa vào mọi thực thể có một giá trị khóa mà nó sử dụng để theo dõi các thực thể. Một trong những quy ước mà Code First phụ thuộc vào là cách nó ngụ ý thuộc tính nào là khóa trong mỗi lớp Code First.

  • Quy ước là tìm kiếm một thuộc tính có tên “Id” hoặc một thuộc tính kết hợp tên lớp và “Id”, chẳng hạn như “StudentId”.

  • Thuộc tính sẽ ánh xạ đến một cột khóa chính trong cơ sở dữ liệu.

  • Các lớp Sinh viên, Khóa học và Ghi danh tuân theo quy ước này.

Bây giờ, hãy giả sử lớp Sinh viên sử dụng tên StdntID thay vì ID. Khi Code First không tìm thấy thuộc tính phù hợp với quy ước này, nó sẽ đưa ra một ngoại lệ vì yêu cầu của Entity Framework là bạn phải có thuộc tính khóa. Bạn có thể sử dụng chú thích khóa để chỉ định thuộc tính nào sẽ được sử dụng làm EntityKey.

Hãy xem đoạn mã sau của một lớp Sinh viên có chứa StdntID, nhưng nó không tuân theo quy ước mặc định của Code First. Vì vậy, để xử lý điều này, một thuộc tính Khóa được thêm vào sẽ làm cho nó trở thành khóa chính.

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

Khi bạn chạy ứng dụng của mình và xem xét cơ sở dữ liệu của mình trong SQL Server Explorer, bạn sẽ thấy rằng khóa chính bây giờ là StdntID trong bảng Học sinh.

Entity Framework cũng hỗ trợ các khóa tổng hợp. Composite keyscũng là các khóa chính bao gồm nhiều thuộc tính. Ví dụ: bạn có một lớp DrivingLicense có khóa chính là sự kết hợp của LicenseNumber và Iss IssueCountry.

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

Khi bạn có các khóa tổng hợp, Entity Framework yêu cầu bạn xác định thứ tự của các thuộc tính khóa. Bạn có thể thực hiện việc này bằng cách sử dụng chú thích Cột để chỉ định đơn hàng.

Dấu thời gian

Code First sẽ coi thuộc tính Dấu thời gian giống như thuộc tính ConcurrencyCheck, nhưng nó cũng sẽ đảm bảo rằng trường cơ sở dữ liệu mà mã tạo ra đầu tiên là không thể null.

  • Phổ biến hơn là sử dụng trường chuyển đổi hàng hoặc trường dấu thời gian để kiểm tra đồng thời.

  • Thay vì sử dụng chú thích ConcurrencyCheck, bạn có thể sử dụng chú thích TimeStamp cụ thể hơn miễn là loại thuộc tính là mảng byte.

  • Bạn chỉ có thể có một thuộc tính dấu thời gian trong một lớp nhất định.

Hãy xem một ví dụ đơn giản bằng cách thêm thuộc tính TimeStamp vào lớp Khóa học -

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

Như bạn có thể thấy trong ví dụ trên, thuộc tính Dấu thời gian được áp dụng cho thuộc tính Byte [] của lớp Khóa học. Vì vậy, Code First sẽ tạo một cột dấu thời gian TStamptrong bảng Khóa học.

Kiểm tra đồng thời

Chú thích ConcurrencyCheck cho phép bạn gắn cờ một hoặc nhiều thuộc tính được sử dụng để kiểm tra đồng thời trong cơ sở dữ liệu khi người dùng chỉnh sửa hoặc xóa một thực thể. Nếu bạn đã làm việc với EF Designer, điều này phù hợp với việc đặt ConcurrencyMode của thuộc tính thành Cố định.

Hãy xem một ví dụ đơn giản về cách ConcurrencyCheck hoạt động bằng cách thêm nó vào thuộc tính Title trong lớp Course.

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

Trong lớp Khóa học ở trên, thuộc tính ConcurrencyCheck được áp dụng cho thuộc tính Title hiện có. Bây giờ, Code First sẽ bao gồm cột Title trong lệnh cập nhật để kiểm tra tính đồng thời lạc quan như được hiển thị trong đoạn mã sau.

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

Chú thích Bắt buộc

Chú thích Bắt buộc cho EF biết rằng một thuộc tính cụ thể là bắt buộc. Chúng ta hãy xem lớp Sinh viên sau, trong đó id Bắt buộc được thêm vào thuộc tính FirstMidName. Thuộc tính bắt buộc sẽ buộc EF phải đảm bảo rằng thuộc tính có dữ liệu trong đó.

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

Như đã thấy trong ví dụ trên, thuộc tính Bắt buộc được áp dụng cho FirstMidName và LastName. Vì vậy, Code First sẽ tạo một cột NOT NULL FirstMidName và LastName trong bảng Students như thể hiện trong hình sau.

MaxLength

Thuộc tính MaxLength cho phép bạn chỉ định xác nhận thuộc tính bổ sung. Nó có thể được áp dụng cho thuộc tính kiểu chuỗi hoặc mảng của một lớp miền. EF Code First sẽ đặt kích thước của một cột như được chỉ định trong thuộc tính MaxLength.

Chúng ta hãy xem lớp Khóa học sau, trong đó thuộc tính MaxLength (24) được áp dụng cho thuộc tính 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; }
}

Khi bạn chạy ứng dụng trên, Code First sẽ tạo một tiêu đề cột nvarchar (24) trong bảng CourseId như thể hiện trong hình sau.

Khi người dùng đặt Tiêu đề chứa hơn 24 ký tự, thì EF sẽ ném EntityValidationError.

Độ dài nhỏ nhất

Thuộc tính MinLength cũng cho phép bạn chỉ định xác nhận thuộc tính bổ sung, giống như bạn đã làm với MaxLength. Thuộc tính MinLength cũng có thể được sử dụng với thuộc tính MaxLength như được hiển thị trong đoạn mã sau.

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

EF sẽ ném EntityValidationError, nếu bạn đặt giá trị của thuộc tính Title nhỏ hơn độ dài được chỉ định trong thuộc tính MinLength hoặc lớn hơn độ dài được chỉ định trong thuộc tính MaxLength.

Chiều dài chuỗi

StringLength cũng cho phép bạn chỉ định các xác nhận thuộc tính bổ sung như MaxLength. Sự khác biệt duy nhất là thuộc tính StringLength chỉ có thể được áp dụng cho một thuộc tính kiểu chuỗi của các lớp Miền.

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 cũng xác nhận giá trị của một thuộc tính cho thuộc tính StringLength. Nếu người dùng đặt Tiêu đề chứa nhiều hơn 24 ký tự, thì EF sẽ ném EntityValidationError.

Bàn

Mã mặc định Quy ước đầu tiên tạo ra một tên bảng tương tự như tên lớp. Nếu bạn đang cho phép Code First tạo cơ sở dữ liệu và cũng muốn thay đổi tên của các bảng mà nó đang tạo. Sau đó -

  • Bạn có thể sử dụng Code First với cơ sở dữ liệu hiện có. Nhưng không phải lúc nào tên của các lớp cũng khớp với tên của các bảng trong cơ sở dữ liệu của bạn.

  • Thuộc tính bảng ghi đè quy ước mặc định này.

  • EF Code First sẽ tạo một bảng có tên được chỉ định trong thuộc tính Table cho một lớp miền nhất định.

Hãy xem ví dụ sau trong đó lớp được đặt tên là Sinh viên và theo quy ước, Code First cho rằng điều này sẽ ánh xạ tới một bảng có tên là Sinh viên. Nếu không phải như vậy, bạn có thể chỉ định tên của bảng với thuộc tính Table như được hiển thị trong đoạn mã sau.

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

Bây giờ bạn có thể thấy rằng thuộc tính Table chỉ định bảng là StudentsInfo. Khi bảng được tạo, bạn sẽ thấy tên bảng là StudentsInfo như trong hình sau.

Bạn không thể chỉ định tên bảng mà còn có thể chỉ định một lược đồ cho bảng bằng cách sử dụng thuộc tính Table như được hiển thị trong đoạn mã sau.

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

Bạn có thể thấy trong ví dụ trên, bảng được chỉ định bằng lược đồ quản trị. Bây giờ Code First sẽ tạo bảng StudentsInfo trong lược đồ Quản trị như thể hiện trong hình sau.

Cột

Nó cũng giống như thuộc tính Table, nhưng thuộc tính Table ghi đè hành vi bảng trong khi thuộc tính Column ghi đè hành vi cột. Mã mặc định Quy ước đầu tiên tạo một tên cột tương tự như tên thuộc tính. Nếu bạn đang cho phép Code First tạo cơ sở dữ liệu và cũng muốn thay đổi tên của các cột trong bảng của bạn. Sau đó -

  • Thuộc tính cột ghi đè quy ước mặc định.

  • EF Code First sẽ tạo một cột có tên được chỉ định trong thuộc tính Column cho một thuộc tính nhất định.

Hãy xem ví dụ sau, trong đó thuộc tính được đặt tên là FirstMidName và theo quy ước, Code First cho rằng điều này sẽ ánh xạ tới một cột có tên FirstMidName.

Nếu không phải như vậy, bạn có thể chỉ định tên của cột bằng thuộc tính Column như được hiển thị trong đoạn mã sau.

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

Bạn có thể thấy rằng thuộc tính Column chỉ định cột là FirstName. Khi bảng được tạo, bạn sẽ thấy tên cột FirstName như trong hình sau.

Mục lục

Thuộc tính Index đã được giới thiệu trong Entity Framework 6.1. Nếu bạn đang sử dụng phiên bản cũ hơn, thông tin trong phần này không áp dụng.

  • Bạn có thể tạo chỉ mục trên một hoặc nhiều cột bằng IndexAttribute.

  • Việc thêm thuộc tính vào một hoặc nhiều thuộc tính sẽ khiến EF tạo chỉ mục tương ứng trong cơ sở dữ liệu khi nó tạo cơ sở dữ liệu.

  • Các chỉ mục giúp cho việc truy xuất dữ liệu nhanh hơn và hiệu quả, trong hầu hết các trường hợp. Tuy nhiên, việc quá tải một bảng hoặc dạng xem với các chỉ mục có thể ảnh hưởng đến hiệu suất của các hoạt động khác như chèn hoặc cập nhật.

  • Lập chỉ mục là tính năng mới trong Entity Framework nơi bạn có thể cải thiện hiệu suất của ứng dụng Code First của mình bằng cách giảm thời gian cần thiết để truy vấn dữ liệu từ cơ sở dữ liệu.

  • Bạn có thể thêm chỉ mục vào cơ sở dữ liệu của mình bằng cách sử dụng thuộc tính Chỉ mục và ghi đè các cài đặt Duy nhất và Nhóm mặc định để có được chỉ mục phù hợp nhất với tình huống của bạn.

  • Theo mặc định, chỉ mục sẽ được đặt tên là IX_ <tên thuộc tính>

Chúng ta hãy xem đoạn mã sau, trong đó thuộc tính Chỉ mục được thêm vào trong lớp Khóa học cho Tín chỉ.

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

Bạn có thể thấy rằng thuộc tính Chỉ mục được áp dụng cho thuộc tính Tín dụng. Khi bảng được tạo, bạn sẽ thấy IX_Credits trong Chỉ mục.

Theo mặc định, các chỉ mục không phải là duy nhất, nhưng bạn có thể sử dụng IsUniquetham số được đặt tên để chỉ định rằng một chỉ mục phải là duy nhất. Ví dụ sau đây giới thiệu một chỉ mục duy nhất như được hiển thị trong đoạn mã sau.

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

Quy ước Code First sẽ xử lý các mối quan hệ phổ biến nhất trong mô hình của bạn, nhưng có một số trường hợp cần trợ giúp. Ví dụ: bằng cách thay đổi tên của thuộc tính khóa trong lớp Sinh viên đã tạo ra một vấn đề với mối quan hệ của nó với lớp Đăng ký.

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

Trong khi tạo cơ sở dữ liệu, Code First nhìn thấy thuộc tính StudentID trong lớp Enrollment và nhận dạng nó, theo quy ước rằng nó khớp với tên lớp cộng với “ID”, làm khóa ngoại cho lớp Student. Tuy nhiên, không có thuộc tính StudentID trong lớp Sinh viên, nhưng thuộc tính StdntID là lớp Sinh viên.

Giải pháp cho việc này là tạo thuộc tính điều hướng trong Enrollment và sử dụng ForeignKey DataAnnotation để giúp Code First hiểu cách xây dựng mối quan hệ giữa hai lớp như được hiển thị trong đoạn mã sau.

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

Bây giờ bạn có thể thấy rằng thuộc tính ForeignKey được áp dụng cho thuộc tính điều hướng.

NotMapped

Theo quy ước mặc định của Code First, mọi thuộc tính thuộc kiểu dữ liệu được hỗ trợ và bao gồm getters và setters đều được thể hiện trong cơ sở dữ liệu. Nhưng điều này không phải lúc nào cũng đúng trong các ứng dụng của bạn. Thuộc tính NotMapped ghi đè quy ước mặc định này. Ví dụ: bạn có thể có một thuộc tính trong lớp Sinh viên như FatherName, nhưng nó không cần phải được lưu trữ. Bạn có thể áp dụng thuộc tính NotMapped cho thuộc tính FatherName mà bạn không muốn tạo một cột trong cơ sở dữ liệu như được hiển thị trong đoạn mã sau.

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

Bạn có thể thấy rằng thuộc tính NotMapped được áp dụng cho thuộc tính FatherName. Khi bảng được tạo, bạn sẽ thấy rằng cột FatherName sẽ không được tạo trong cơ sở dữ liệu, nhưng nó hiện diện trong lớp Sinh viên.

Code First sẽ không tạo một cột cho thuộc tính, không có cột hoặc bộ định tuyến như được hiển thị trong ví dụ sau về thuộc tính Địa chỉ và Tuổi của lớp Sinh viên.

InverseProperty

InverseProperty được sử dụng khi bạn có nhiều mối quan hệ giữa các lớp. Trong lớp Ghi danh, bạn có thể muốn theo dõi những ai đã đăng ký Khóa học hiện tại và Khóa học trước đó. Hãy thêm hai thuộc tính điều hướng cho lớp 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; }
}

Tương tự, bạn cũng sẽ cần thêm lớp Khóa học được tham chiếu bởi các thuộc tính này. Lớp Khóa học có các thuộc tính điều hướng trở lại Lớp đăng ký, lớp này chứa tất cả các đăng ký hiện tại và trướ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 tạo cột khóa ngoại {Class Name} _ {Primary Key}, nếu thuộc tính khóa ngoại không được bao gồm trong một lớp cụ thể như được hiển thị trong các lớp trên. Khi cơ sở dữ liệu được tạo, bạn sẽ thấy các khóa ngoại sau.

Như bạn có thể thấy rằng Code đầu tiên không thể tự khớp các thuộc tính trong hai lớp. Bảng cơ sở dữ liệu cho Đăng ký phải có một khóa ngoại cho CurrCourse và một cho PrevCourse, nhưng Code First sẽ tạo bốn thuộc tính khóa ngoại, tức là

  • CurrCourse _CourseID
  • PrevCourse _CourseID
  • Course_CourseID và
  • Course_CourseID1

Để khắc phục những sự cố này, bạn có thể sử dụng chú thích InverseProperty để chỉ định căn chỉnh của các thuộc tính.

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

Như bạn có thể thấy thuộc tính InverseProperty được áp dụng trong lớp Khóa học ở trên bằng cách chỉ định thuộc tính tham chiếu nào của lớp Đăng ký. Bây giờ, Code First sẽ tạo một cơ sở dữ liệu và chỉ tạo hai cột khóa ngoại trong bảng Enrollments như thể hiện trong hình sau.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.