Khung thực thể - Ví dụ đầu tiên

Hãy xác định một mô hình rất đơn giản bằng cách sử dụng các lớp. Chúng tôi chỉ định nghĩa chúng trong tệp Program.cs nhưng trong một ứng dụng thực tế, bạn sẽ chia các lớp của mình thành các tệp riêng biệt và có khả năng là một dự án riêng biệt. Sau đây là một mô hình dữ liệu mà chúng tôi sẽ tạo bằng cách sử dụng phương pháp Code First.

Tạo mô hình

Thêm ba lớp sau vào tệp Program.cs bằng cách sử dụng mã sau cho lớp Sinh viên.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}
  • Thuộc tính ID sẽ trở thành cột khóa chính của bảng cơ sở dữ liệu tương ứng với lớp này.

  • Thuộc tính Enrollments là một thuộc tính điều hướng. Thuộc tính điều hướng giữ các thực thể khác có liên quan đến thực thể này.

  • Trong trường hợp này, thuộc tính Ghi danh của một thực thể Sinh viên sẽ chứa tất cả các thực thể Ghi danh có liên quan đến thực thể Sinh viên đó.

  • Các thuộc tính điều hướng thường được định nghĩa là ảo để chúng có thể tận dụng một số chức năng của Entity Framework chẳng hạn như tải chậm.

  • Nếu một thuộc tính điều hướng có thể chứa nhiều thực thể (như trong mối quan hệ nhiều-nhiều hoặc một-nhiều), thì loại của nó phải là danh sách trong đó các mục nhập có thể được thêm, xóa và cập nhật, chẳng hạn như ICollection.

Sau đây là cách thực hiện cho lớp Khóa học.

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Thuộc tính Enrollments là một thuộc tính điều hướng. Thực thể Khóa học có thể liên quan đến bất kỳ số lượng thực thể Đăng ký nào.

Sau đây là cách thực hiện cho Enrollment class và enum.

public enum Grade {
   A, B, C, D, F
}

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; }
}
  • Thuộc tính EnrollmentID sẽ là khóa chính.

  • Thuộc tính Grade là một enum. Dấu chấm hỏi sau khai báo loại Lớp cho biết thuộc tính Lớp là không thể bỏ qua.

  • Điểm rỗng khác với điểm 0. Không có nghĩa là một điểm chưa được biết hoặc chưa được chỉ định.

  • Thuộc tính StudentID và CourseID là các khóa ngoại, và các thuộc tính điều hướng tương ứng là Student và Course.

  • Thực thể Ghi danh được liên kết với một Sinh viên và một thực thể Khóa học, vì vậy thuộc tính chỉ có thể chứa một thực thể Sinh viên và Khóa học duy nhất.

Tạo bối cảnh cơ sở dữ liệu

Lớp chính điều phối chức năng Entity Framework cho một mô hình dữ liệu nhất định là lớp ngữ cảnh cơ sở dữ liệu cho phép truy vấn và lưu dữ liệu. Bạn có thể tạo lớp này bằng cách lấy từ lớp DbContext và hiển thị một DbSet đã nhập cho mỗi lớp trong mô hình của chúng tôi. Sau đây là cách triển khai trên lớp MyContext, có nguồn gốc từ lớp DbContext.

public class MyContext : DbContext {
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Sau đây là mã hoàn chỉnh trong tệp Program.cs.

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

   public enum Grade {
      A, B, C, D, F
   }

   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 {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public DateTime EnrollmentDate { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

Đoạn mã trên là tất cả những gì chúng ta cần để bắt đầu lưu trữ và truy xuất dữ liệu. Hãy thêm một số dữ liệu và sau đó truy xuất nó. Sau đây là mã trong phương thức chính.

static void Main(string[] args) {

   using (var context = new MyContext()) {
      // Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", LastName = "Bomer", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student);
		
      var student1 = new Student {
         FirstMidName = "Mark", LastName = "Upston", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student1);
      context.SaveChanges();

      // Display all Students from the database
      var students = (from s in context.Students 
         orderby s.FirstMidName select s).ToList<Student>();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }
		
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau.

Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...

Bây giờ câu hỏi xuất hiện trong đầu là, đâu là dữ liệu và cơ sở dữ liệu mà chúng ta đã thêm một số dữ liệu và sau đó truy xuất nó từ cơ sở dữ liệu. Theo quy ước, DbContext đã tạo một cơ sở dữ liệu cho bạn.

  • Nếu một phiên bản SQL Express cục bộ có sẵn thì Code First đã tạo cơ sở dữ liệu trên phiên bản đó.

  • Nếu SQL Express không khả dụng, thì Code First sẽ thử và sử dụng LocalDb.

  • Cơ sở dữ liệu được đặt tên theo tên đủ điều kiện của ngữ cảnh dẫn xuất.

Trong trường hợp của chúng tôi, phiên bản SQL Express có sẵn và tên cơ sở dữ liệu là EFCodeFirstDemo.MyContext như thể hiện trong hình sau.

  • Đây chỉ là những quy ước mặc định và có nhiều cách khác nhau để thay đổi cơ sở dữ liệu mà Code First sử dụng.

  • Như bạn có thể thấy trong hình trên, nó đã tạo ra các bảng Sinh viên, Khóa học và Đăng ký và mỗi bảng chứa các cột có kiểu dữ liệu và độ dài thích hợp.

  • Tên cột và kiểu dữ liệu cũng khớp với thuộc tính của các lớp miền tương ứng.

Khởi tạo cơ sở dữ liệu

Trong ví dụ trên, chúng ta đã thấy Code First tạo cơ sở dữ liệu tự động, nhưng nếu bạn muốn thay đổi tên của cơ sở dữ liệu và máy chủ, hãy xem cách Code First quyết định tên cơ sở dữ liệu và máy chủ trong khi khởi tạo cơ sở dữ liệu. Hãy xem sơ đồ sau.

Bạn có thể xác định hàm tạo cơ sở của lớp ngữ cảnh theo những cách sau.

  • Không có thông số
  • Tên cơ sở dữ liệu
  • Tên chuỗi kết nối

Không có thông số

Nếu bạn chỉ định phương thức khởi tạo cơ sở của lớp ngữ cảnh mà không có bất kỳ tham số nào như được hiển thị trong ví dụ trên, thì khung thực thể sẽ tạo cơ sở dữ liệu trong máy chủ SQLEXPRESS cục bộ của bạn với tên {Namespace}. {Context class name}.

Trong ví dụ trên, cơ sở dữ liệu được tạo tự động có tên là EFCodeFirstDemo.MyContext. Nếu bạn nhìn vào tên, bạn sẽ thấy rằng EFCodeFirstDemo là không gian tên và MyContext là tên lớp ngữ cảnh như được hiển thị trong đoạn mã sau.

public class MyContext : DbContext {
   public MyContext() : base() {}

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Tên cơ sở dữ liệu

Nếu bạn chuyển tên cơ sở dữ liệu làm tham số trong phương thức khởi tạo cơ sở của lớp ngữ cảnh, thì Code First sẽ tự động tạo lại cơ sở dữ liệu, nhưng lần này tên sẽ là tham số được truyền dưới dạng tham số trong phương thức khởi tạo cơ sở trên máy chủ cơ sở dữ liệu SQLEXPRESS cục bộ .

Trong đoạn mã sau, MyContextDB được chỉ định làm tham số trong hàm tạo cơ sở. Nếu chạy ứng dụng của bạn, thì cơ sở dữ liệu có tên MyContextDB sẽ được tạo trong máy chủ SQL cục bộ của bạn.

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Tên chuỗi kết nối

Đây là một cách dễ dàng để yêu cầu DbContext sử dụng máy chủ cơ sở dữ liệu khác với SQL Express hoặc LocalDb. Bạn có thể chọn đặt một chuỗi kết nối trong tệp app.config của mình.

  • Nếu tên của chuỗi kết nối khớp với tên của ngữ cảnh của bạn (có hoặc không có đủ điều kiện không gian tên), thì nó sẽ được DbContext tìm thấy khi hàm tạo ít tham số được sử dụng.

  • Nếu tên chuỗi kết nối khác với tên ngữ cảnh của bạn, thì bạn có thể yêu cầu DbContext sử dụng kết nối này trong chế độ Code First bằng cách chuyển tên chuỗi kết nối đến phương thức khởi tạo DbContext.

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}
  • Trong đoạn mã trên, đoạn mã của chuỗi kết nối lớp ngữ cảnh được chỉ định làm tham số trong hàm tạo cơ sở.

  • Tên chuỗi kết nối phải bắt đầu bằng "name =" nếu không, nó sẽ coi nó như một tên cơ sở dữ liệu.

  • Biểu mẫu này nói rõ rằng bạn muốn tìm thấy chuỗi kết nối trong tệp cấu hình của mình. Một ngoại lệ sẽ được ném ra nếu không tìm thấy một chuỗi kết nối với tên đã cho.

<connectionStrings>
   <add name = "MyContextDB"
      connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
      providerName = "System.Data.SqlClient"/>
</connectionStrings>
  • Tên cơ sở dữ liệu trong chuỗi kết nối trong app.config là EFMyContextDB. CodeFirst sẽ tạo mộtEFMyContextDB cơ sở dữ liệu hoặc sử dụng hiện có EFMyContextDB cơ sở dữ liệu tại SQL Server cục bộ.

Các lớp miền

Cho đến nay, chúng tôi chỉ cho phép EF khám phá mô hình bằng cách sử dụng các quy ước mặc định của nó, nhưng sẽ có lúc các lớp của chúng tôi không tuân theo các quy ước và chúng tôi cần có thể thực hiện cấu hình thêm. Nhưng bạn có thể ghi đè các quy ước này bằng cách định cấu hình các lớp miền của mình để cung cấp cho EF thông tin cần thiết. Có hai tùy chọn để định cấu hình các lớp miền của bạn -

  • Chú thích dữ liệu
  • API thông thạo

Chú thích dữ liệu

DataAnnotations được sử dụng để định cấu hình các lớp của bạn 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 thực phía máy khách.

Sau đây là các chú thích dữ liệu được sử dụng trong lớp sinh viên.

public class Enrollment {

   [Key]
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   [ForeignKey("CourseID")]
   public virtual Course Course { get; set; }

   [ForeignKey("ID")]
   public virtual Student Student { get; set; }
}

API thông thạo

Hầu hết cấu hình mô hình có thể được thực hiện bằng cách sử dụng chú thích dữ liệu đơn giản. API thông thạo là một cách nâng cao để chỉ định cấu hình mô hình bao gồm mọi thứ mà chú thích dữ liệu có thể làm, ngoài một số cấu hình nâng cao hơn không thể thực hiện với chú thích dữ liệu. Chú thích dữ liệu và API thông thạo có thể được sử dụng cùng nhau.

Để truy cập API thông thạo, bạn ghi đè phương thức OnModelCreating trong DbContext. Bây giờ chúng ta hãy đổi tên cột trong bảng sinh viên từ FirstMidName thành FirstName như được hiển thị trong đoạn mã sau.

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
         .HasColumnName("FirstName");
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}