Entity Framework Code First toàn tập

  • Trung Nguyen
  • 28/04/2020

  • 29 min read

Code First là gì?

Entity Framework đã trình làng cách tiếp cận Code First trong Entity Framework 4.1. Code First hầu hết có ích trong Thiết kế hướng domain .

Trong cách tiếp cận Code First, bạn tập trung vào miền của ứng dụng và bắt đầu tạo các lớp cho thực thể miền thay vì thiết kế cơ sở dữ liệu trước rồi tạo các lớp khớp với thiết kế cơ sở dữ liệu của bạn. Hình dưới đây minh họa cách tiếp cận Code First.

Code First trong Entity FrameworkNhư bạn hoàn toàn có thể thấy trong hình trên, Entity Framework API sẽ tạo cơ sở tài liệu dựa trên thông số kỹ thuật và những lớp miền của bạn. Điều này có nghĩa là bạn cần khởi đầu viết code trước bằng C # hoặc VB.NET và sau đó Entity Framework ( EF ) sẽ tạo cơ sở tài liệu từ code của bạn .

Quy trình làm việc với Code First

Hình dưới đây minh họa quá trình tăng trưởng Code First .Quy trình làm việc với Code FirstQuy trình tăng trưởng theo cách tiếp cận Code First sẽ là : Tạo hoặc sửa đổi những lớp miền -> thông số kỹ thuật những lớp miền này bằng những thuộc tính chú thích tài liệu hoặc Fluent API -> Tạo hoặc update lược đồ cơ sở tài liệu bằng automated migration hoặc code-based migration .

Các quy ước trong Code First

Các quy ước là những bộ quy tắc mặc định tự động hóa thông số kỹ thuật một quy mô khái niệm dựa trên những lớp miền của bạn khi thao tác với cách tiếp cận Code First .Như bạn đã thấy trong ví dụ ở trên, Entity Framework API đã thông số kỹ thuật những khóa chính, khóa ngoại, những mối quan hệ, kiểu tài liệu của cột, v.v. từ những lớp miền mà không cần thông số kỹ thuật bổ trợ .Điều này là do những quy ước của Entity Framework Code First. Nếu những lớp miền của bạn tuân theo những quy ước thì lược đồ cơ sở tài liệu sẽ được thông số kỹ thuật dựa trên những quy ước này .Các quy ước trong Entity Framework 6. x Code First được định nghĩa trong namespace System. Data. Entity. ModelConfiguration. Conventions .Bảng sau liệt kê những quy ước mặc định của Code First :

Quy ước Miêu tả
Lược đồ Theo mặc định, EF tạo tất cả các đối tượng DB vào lược đồ dbo .
Tên bảng + ‘s’

EF sẽ tạo bảng DB với tên lớp thực thể thêm ‘s’ ở cuối, ví dụ: lớp Student sẽ ánh xạ tới bảng Students.
Tên khóa chính 1) Id

2) + “Id” (không phân biệt chữ hoa chữ thường)

EF sẽ tạo cột khóa chính cho thuộc tính có tên Id hoặc + “Id” (không phân biệt chữ hoa chữ thường).

Tên khóa ngoại Theo mặc định, EF sẽ tìm thuộc tính khóa ngoại có cùng tên với tên khóa chính của thực thể chính.

Nếu thuộc tính khóa ngoại không tồn tại, thì EF sẽ tạo cột khóa ngoại trong bảng Db với + “_” +

ví dụ: EF sẽ tạo cột khóa ngoại Grade_GradeId trong bảng Students nếu thực thể Student không chứa thuộc tính khóa ngoại cho Grade.
Cột Null EF tạo một cột null cho tất cả các thuộc tính kiểu tham chiếu và các thuộc tính kiểu nguyên thủy nullable, ví dụ: string, Nullable , Student, Grade (tất cả các thuộc tính kiểu lớp)
Cột Not Null EF tạo các cột Not Null cho các thuộc tính khóa chính và các thuộc tính kiểu giá trị không nullable, ví dụ: int, float, binary, datetime, v.v.
Thứ tự cột EF sẽ tạo các cột theo cùng thứ tự như các thuộc tính trong một lớp thực thể. Tuy nhiên, các cột khóa chính sẽ được di chuyển lên đầu tiên.
Ánh xạ thuộc tính vào DB Theo mặc định, tất cả các thuộc tính sẽ ánh xạ tới cơ sở dữ liệu. Sử dụng thuộc tính [NotMapped] để loại trừ thuộc tính hoặc lớp không ánh xạ vào DB.
Cascade delete Được bật theo mặc định cho tất cả các loại mối quan hệ.

Bảng sau liệt kê kiểu tài liệu C # được ánh xạ với kiểu tài liệu SQL Server .

Kiểu dữ liệu C# Ánh xạ tới kiểu dữ liệu SQL Server
int int
string nvarchar(Max)
decimal decimal(18,2)
float real
byte[] varbinary(Max)
datetime datetime
bool bit
byte tinyint
short smallint
long bigint
double float
char Không ánh xạ
sbyte Không ánh xạ (ném ra exception)
object Không ánh xạ

Hình dưới đây minh họa ánh xạ quy ước với cơ sở tài liệu .Các quy ước trong Code First

Quy ước về mối quan hệ

Entity Framework 6 tạo mối quan hệ một-nhiều bằng cách sử dụng thuộc tính điều hướng theo quy ước mặc định. Phần này sẽ được trình diễn chi tiết cụ thể trong bài viết khác .

Lưu ý: Entity Framework 6 không có các quy ước mặc định cho các mối quan hệ một-một và nhiều-nhiều. Bạn cần cấu hình chúng bằng Fluent API hoặc DataAnnotation.

Quy ước kiểu phức tạp

Code First tạo kiểu phức tạp cho lớp không gồm có thuộc tính khóa và khóa chính không được ĐK bằng thuộc tính DataAnnotation hoặc Fluent API .Phần này trình diễn tổng quan về những quy ước trong Code First. Các quy ước này hoàn toàn có thể được ghi đè bằng những sử dụng thuộc tính DataAnnotation hoặc Fluent API .

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

Ở phần này, tất cả chúng ta sẽ tìm hiểu và khám phá cách Entity Framework quyết định hành động tên cơ sở tài liệu và sever trong khi khởi tạo cơ sở tài liệu theo cách tiếp cận Code First .Hình dưới đây cho thấy một quy trình tiến độ khởi tạo cơ sở tài liệu, dựa trên tham số được truyền trong phương pháp khởi tạo cơ sở của lớp Context được thừa kế từ lớp DbContext :Khởi tạo cơ sở dữ liệu trong Entity Framework

Tham số khởi tạo cơ sở dữ liệu

Theo hình trên, phương pháp khởi tạo cơ sở của lớp Context hoàn toàn có thể có tham số sau :

  1. Không có tham số.
  2. Tên cơ sở dữ liệu.
  3. Tên chuỗi kết nối.

Không có tham số

Nếu bạn không chỉ định tham số trong phương pháp khởi tạo cơ sở của lớp Context thì nó sẽ tạo cơ sở tài liệu trong sever SQLEXPRESS cục bộ của bạn với tên khớp với { Namespace }. { Tên lớp Context } .

Ví dụ, Entity Framework (EF) sẽ tạo một cơ sở dữ liệu có tên SchoolDataLayer.Context cho lớp Context sau:

namespace SchoolDataLayer
{
    public class Context: DbContext 
    {
        public Context(): base()
        { }
    }
}

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

Bạn cũng hoàn toàn có thể chỉ định tên cơ sở tài liệu làm tham số trong phương pháp khởi tạo cơ sở của lớp Context .Nếu bạn chỉ định tham số tên cơ sở tài liệu, thì Code First sẽ tạo cơ sở tài liệu với tên bạn đã chỉ định trong phương pháp khởi tạo cơ sở trong sever cơ sở tài liệu SQLEXPRESS cục bộ .Ví dụ, Code First sẽ tạo cơ sở tài liệu có tên MySchoolDB cho lớp Context sau .

namespace SchoolDataLayer
{
    public class Context: DbContext 
    {
        public Context(): base("MySchoolDB") 
        { }
    }
}

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

Bạn cũng hoàn toàn có thể chỉ định chuỗi liên kết trong app.config hoặc web.config và chỉ định tên chuỗi liên kết khởi đầu bằng ” name = ” trong phương pháp khởi tạo cơ sở của lớp Context .

Hãy xem xét ví dụ sau nơi chúng ta truyền tham số name=SchoolDBConnectionString trong phương thức khởi tạo cơ sở.

namespace SchoolDataLayer
{
    public class Context: DbContext 
    {
        public SchoolDBContext() : base("name=SchoolDBConnectionString") 
        {
        }
    }
}

App. config :



    
        
    

Trong lớp Context ở trên, chúng tôi chỉ định tên chuỗi liên kết làm tham số. Xin quan tâm rằng tên chuỗi liên kết phải mở màn bằng ” name = “, nếu không, nó sẽ coi nó là tên cơ sở tài liệu .Tên cơ sở tài liệu trong chuỗi liên kết trong App. config là SchoolDB. Entity Framework ( EF ) sẽ tạo cơ sở tài liệu SchoolDB mới hoặc sử dụng cơ sở tài liệu SchoolDB hiện có trong SQL Server cục bộ .

Đảm bảo rằng bạn có thuộc tính providerName="System.Data.SqlClient" cho cơ sở dữ liệu SQL Server trong chuỗi kết nối.

Chiến lược khởi tạo cơ sở dữ liệu

Bạn đã tạo cơ sở tài liệu sau khi chạy ứng dụng Code First lần tiên phong, nhưng lần thứ hai trở đi thì sao ? Nó sẽ tạo ra một cơ sở tài liệu mới mỗi khi bạn chạy ứng dụng ?Còn thiên nhiên và môi trường Production thì sao ? Làm thế nào để bạn update cơ sở tài liệu khi bạn biến hóa quy mô miền của bạn ?Để giải quyết và xử lý những ngữ cảnh này, bạn phải sử dụng một trong những kế hoạch khởi tạo cơ sở tài liệu .Có bốn kế hoạch khởi tạo cơ sở tài liệu khác nhau trong Code First :

  1. CreateDatabaseIfNotExists: Đây là trình khởi tạo mặc định. Như tên gọi, nó sẽ tạo cơ sở dữ liệu nếu không tồn tại theo cấu hình. Tuy nhiên, nếu bạn thay đổi lớp mô hình và sau đó chạy ứng dụng với trình khởi tạo này, thì nó sẽ đưa ra một ngoại lệ.
  2. DropCreateDatabaseIfModelChanges: Trình khởi tạo này xóa cơ sở dữ liệu hiện có và tạo cơ sở dữ liệu mới, nếu các lớp mô hình của bạn (các lớp thực thể) đã bị thay đổi. Vì vậy, bạn không phải lo lắng về việc duy trì lược đồ cơ sở dữ liệu của mình, khi các lớp mô hình của bạn thay đổi.
  3. DropCreateDatabaseAlways: Như tên cho thấy, trình khởi tạo này sẽ xóa cơ sở dữ liệu hiện có mỗi khi bạn chạy ứng dụng, bất kể các lớp mô hình của bạn có thay đổi hay không. Điều này sẽ hữu ích khi bạn muốn có một cơ sở dữ liệu mới mỗi khi bạn chạy ứng dụng, ví dụ như khi bạn đang phát triển ứng dụng.
  4. Trình khởi tạo cơ sở dữ liệu tùy chỉnh: Bạn cũng có thể tạo trình khởi tạo tùy chỉnh của riêng mình, nếu các cách trên không đáp ứng yêu cầu của bạn hoặc bạn muốn thực hiện một số quy trình khác để khởi tạo cơ sở dữ liệu bằng trình khởi tạo ở trên.

Xem thêm về khởi tạo cơ sở tài liệu trong Code First tại đây :

Chiến lược kế thừa trong Code First

Bạn hoàn toàn có thể phong cách thiết kế những lớp thực thể của mình bằng cách sử dụng tính thừa kế trong C # .Trong lập trình hướng đối tượng người tiêu dùng, những lớp có mối quan hệ ” has a ” và ” is a “, trong khi đó quy mô quan hệ dựa trên SQL chỉ có mối quan hệ ” has a ” giữa những bảng .Hệ quản trị cơ sở tài liệu SQL không tương hỗ mối quan hệ ” is a “. Vì vậy, làm thế nào bạn hoàn toàn có thể ánh xạ những lớp thực thể có mối quan hệ ” is a ” vào cơ sở tài liệu quan hệ ?Dưới đây là ba cách tiếp cận khác nhau để ánh xạ thừa kế vào cơ sở tài liệu trong Code First :

  • Table per Hierarchy (TPH): Cách tiếp cận này đề nghị tạo một bảng chung cho toàn bộ các lớp trong phân cấp kế thừa. Bảng này có một cột để phân biệt giữa các lớp con. Đây là một chiến lược ánh xạ kế thừa mặc định trong Entity Framework.
  • Table per Type (TPT): Cách tiếp cận này đề nghị tạo mỗi bảng cho từng lớp trong phân cấp kế thừa (tạo bảng cho cả lớp cha và lớp con).
  • Table per Concrete Class (TPC): Cách tiếp cận này đề nghị tạo mỗi bảng cho từng lớp con trong phân cấp kế thừa, nhưng không tạo bảng cho lớp cha. Vì vậy các thuộc tính của lớp cha sẽ là một phần của mỗi bảng của lớp con.

Xem cụ thể ba cách tiếp cạn để anh xạ thừa kế vào cơ sở tài liệu trong Code First :

Cấu hình các lớp trong Entity Framework

Code First thiết kế xây dựng quy mô khái niệm từ những lớp thực thể của bạn bằng cách sử dụng những quy ước mặc định .EF 6 Code First tận dụng một mẫu lập trình được gọi là quy ước về thông số kỹ thuật. Tuy nhiên, bạn hoàn toàn có thể ghi đè những quy ước này bằng cách thông số kỹ thuật những lớp thực thể của bạn để phân phối cho EF thông tin thiết yếu .Có hai cách để thông số kỹ thuật những lớp thực thể của bạn :

  1. Attribute chú thích dữ liệu.
  2. Fluent API.

Attribute chú thích dữ liệu

Chú thích dữ liệu là một cấu hình dựa trên attribute đơn giản, bạn có thể áp dụng cho các lớp và thuộc tính của nó.

Các attribute này không chỉ dành riêng cho EF mà còn được sử dụng trong ASP.NET Web Form và ASP.NET MVC. Do đó, chúng được đặt trong một namespace riêng biệt là System.ComponentModel.DataAnnotations.

Ví dụ sau đây minh họa việc sử dụng 1 số ít attribute chú thích tài liệu :

[Table("StudentInfo")]
public class Student
{
    public Student() { }
        
    [Key]
    public int SID { get; set; }

    [Column("Name", TypeName="ntext")]
    [MaxLength(20)]
    public string StudentName { get; set; }

    [NotMapped]
    public int? Age { get; set; }        
        
    public int StdId { get; set; }

    [ForeignKey("StdId")]
    public virtual Standard Standard { get; set; }
}

Lưu ý: Attribute chú thích dữ liệu không hỗ trợ tất cả các tùy chọn cấu hình cho Entity Framework. Vì vậy, bạn có thể sử dụng Fluent API, nó cung cấp tất cả các tùy chọn cấu hình cho EF.

Tìm hiểu chi tiết cụ thể về attribute chú thích tài liệu trong Entity Framework tại bài viết này :

Fluent API

Một cách khác để định cấu hình các lớp là bằng cách sử dụng Entity Framework Fluent API. Fluent API dựa trên mẫu thiết kế Fluent API (hay còn gọi là giao diện thông thạo) trong đó kết quả được tạo thành từ một chuỗi phương thức.

Cấu hình Fluent API có thể được áp dụng khi EF xây dựng mô hình từ các lớp thực thể của bạn. Bạn có thể thêm các cấu hình Fluent API bằng cách ghi đè phương thức OnModelCreating của lớp DbContext trong Entity Framework 6.x, như được trình bày bên dưới:

public class SchoolDBContext: DbContext 
{
    public SchoolDBContext(): base("SchoolDBConnectionString") 
    {
    }

    public DbSet Students { get; set; }
    public DbSet Standards { get; set; }
    public DbSet StudentAddress { get; set; }
        
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Configure domain classes using modelBuilder here..
    }
}

Bạn có thể sử dụng tham số modelBuilder, một đối tượng của lớp DbModelBuilder để cấu hình các lớp thực thể của bạn. DbModelBuilder được gọi là Fluent API vì bạn có thể gọi các phương thức khác nhau trong một chuỗi phương thức.

Tìm hiểu cụ thể về Fluent API trong Entity Framework tại bài viết này :

Cấu hình mối quan hệ trong Code First

Trong cơ sở tài liệu quan hệ, một mối quan hệ là một link sống sót giữa những bảng của cơ sở tài liệu quan hệ trải qua những khóa ngoại .Khóa ngoại ( Foreign Key ) là một cột hoặc tổng hợp những cột được sử dụng để thiết lập và thực thi một link giữa tài liệu trong hai bảng .Có ba loại mối quan hệ giữa những bảng và chúng khác nhau tùy thuộc vào cách định nghĩa những cột tương quan .

  • Mối quan hệ một-nhiều
  • Mối quan hệ nhiều-nhiều
  • Mối quan hệ một-một

Để hiểu rõ cách thông số kỹ thuật những mối quan hệ giữa những thực thể trong Entity Framwork Code First bạn hoàn toàn có thể xem bài viết :

Migration trong Code First

Entity Framework Code First có các chiến lược khởi tạo cơ sở dữ liệu khác nhau như CreateDatabaseIfNotExists, DropCreateDatabaseIfModelChanges, và DropCreateDatabaseAlways.

Tuy nhiên, có một số ít yếu tố với những kế hoạch này, ví dụ : nếu bạn đã có tài liệu ( trừ seed data – tài liệu bắt đầu được thêm bằng code ) hoặc stored procedures, triggers, v.v. trong cơ sở tài liệu của bạn .

Các chiến lược này được sử dụng để loại bỏ toàn bộ cơ sở dữ liệu và tạo lại nó, do đó bạn sẽ mất dữ liệu và các đối tượng DB khác.

Entity Framework đã giới thiệu một công cụ migration tự động cập nhật lược đồ cơ sở dữ liệu khi mô hình của bạn thay đổi mà không mất bất kỳ dữ liệu hiện có hoặc các đối tượng cơ sở dữ liệu khác. Nó sử dụng một trình khởi tạo cơ sở dữ liệu mới gọi là MigrateDatabaseToLatestVersion.

Có hai loại Migration :

  1. Migration tự động.
  2. Migration dựa trên mã.

Để hiểu rõ về hai loại Migration trong Code First bạn hoàn toàn có thể tìm hiểu thêm bài viết sau :

Tạo Context và lớp thực thể từ database trong Code First

Ở phần này, bạn sẽ khám phá cách tạo Context và những lớp thực thể từ cơ sở tài liệu có sẵn, sử dụng chiêu thức tiếp cận Code First .Entity Framework phân phối một cách đơn thuần để sử dụng Code First cho cơ sở tài liệu có sẵn. Nó sẽ tạo những lớp thực thể cho toàn bộ những bảng và view trong cơ sở tài liệu hiện tại của bạn và thông số kỹ thuật chúng với những attribute chú thích tài liệu và Fluent API .Để sử dụng Code First cho cơ sở tài liệu có sẵn, nhấp chuột phải vào dự án Bất Động Sản của bạn trong Visual Studio -> Add -> New Item ..Tạo Context và lớp thực thể từ database trong Code First

Chọn ADO.NET Entity Data Model trong hộp thoại Add New Item và đặt tên cho mô hình (đây sẽ là tên lớp Context) rồi nhấp vào nút Add.

Tạo Context và lớp thực thể từ database trong Code First

Điều này sẽ mở trình hướng dẫn Entity Data Model như dưới đây. Chọn Code First from database và nhấn Next.

Tạo Context và lớp thực thể từ database trong Code First

Bây giờ, chọn kết nối dữ liệu cho cơ sở dữ liệu hiện có. Tạo kết nối mới cho cơ sở dữ liệu của bạn nếu danh sách thả xuống không bao gồm kết nối đến cơ sở dữ liệu hiện tại của bạn. Nhấn Next để tiếp tục.

Tạo Context và lớp thực thể từ database trong Code First

Bây giờ, chọn các bảng và view mà bạn muốn tạo các lớp thực thể và nhấp vào Finish.

Tạo Context và lớp thực thể từ database trong Code FirstĐiều này sẽ tạo ra tổng thể những lớp thực thể cho những bảng và view trong cơ sở tài liệu của bạn như được hiển thị bên dưới .Tạo Context và lớp thực thể từ database trong Code FirstNó cũng sẽ tạo lớp Context sau sử dụng Fluent API để thông số kỹ thuật những lớp thực thể theo cơ sở tài liệu của bạn .


namespace EFDemo
{
    using System;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;

    public partial class SchoolContext : DbContext
    {
        public SchoolContext()
            : base("name=SchoolContext2")
        {
        }

        public virtual DbSet Courses { get; set; }
        public virtual DbSet Standards { get; set; }
        public virtual DbSet Students { get; set; }
        public virtual DbSet StudentAddresses { get; set; }
        public virtual DbSet Teachers { get; set; }
        public virtual DbSet View_StudentCourse { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity()
                .Property(e => e.CourseName)
                .IsUnicode(false);

            modelBuilder.Entity()
                .HasMany(e => e.Students)
                .WithMany(e => e.Courses)
                .Map(m => m.ToTable("StudentCourse").MapLeftKey("CourseId").MapRightKey("StudentId"));

            modelBuilder.Entity()
                .Property(e => e.StandardName)
                .IsUnicode(false);

            modelBuilder.Entity()
                .Property(e => e.Description)
                .IsUnicode(false);

            modelBuilder.Entity()
                .HasMany(e => e.Students)
                .WithOptional(e => e.Standard)
                .WillCascadeOnDelete();

            modelBuilder.Entity()
                .HasMany(e => e.Teachers)
                .WithOptional(e => e.Standard)
                .WillCascadeOnDelete();

            modelBuilder.Entity()
                .Property(e => e.StudentName)
                .IsUnicode(false);

            modelBuilder.Entity()
                .Property(e => e.RowVersion)
                .IsFixedLength();

            modelBuilder.Entity()
                .HasOptional(e => e.StudentAddress)
                .WithRequired(e => e.Student)
                .WillCascadeOnDelete();

            modelBuilder.Entity()
                .Property(e => e.Address1)
                .IsUnicode(false);

            modelBuilder.Entity()
                .Property(e => e.Address2)
                .IsUnicode(false);

            modelBuilder.Entity()
                .Property(e => e.City)
                .IsUnicode(false);

            modelBuilder.Entity()
                .Property(e => e.State)
                .IsUnicode(false);

            modelBuilder.Entity()
                .Property(e => e.TeacherName)
                .IsUnicode(false);

            modelBuilder.Entity()
                .HasMany(e => e.Courses)
                .WithOptional(e => e.Teacher)
                .WillCascadeOnDelete();

            modelBuilder.Entity()
                .Property(e => e.StudentName)
                .IsUnicode(false);

            modelBuilder.Entity()
                .Property(e => e.CourseName)
                .IsUnicode(false);
        }
    }
}

Cascade Delete trong Code First

Cascade Delete sẽ tự động hóa xóa những bản ghi phụ thuộc vào hoặc thiết lập những cột khóa ngoài thành phố null khi bản ghi cha bị xóa trong cơ sở tài liệu .Cascade Delete được bật theo mặc định trong Entity Framework cho tổng thể những loại mối quan hệ, ví dụ điển hình như một-một, một-nhiều và nhiều-nhiều .

Cascade Delete trong mối quan hệ một-một

Hãy xem ví dụ sau đây: các thực thể StudentStudentAddress có mối quan hệ một-một.


public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual StudentAddress Address { get; set; }
}
     
public class StudentAddress 
{
    [ForeignKey("Student")]
    public int StudentAddressId { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public int Zipcode { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public virtual Student Student { get; set; }
}

Ví dụ sau đây minh họa cascade delete .


using (var ctx = new SchoolContext()) 
{
    var stud = new Student() { StudentName = "James" };
    var add = new StudentAddress() { Address1 = "address" };

    stud.Address = add;

    ctx.Students.Add(stud);

    ctx.SaveChanges();
    
    ctx.Students.Remove(stud);// student and its address will be removed from db

    ctx.SaveChanges();
}

Trong ví dụ trên, đầu tiên EF lưu đối tượng stud của thực thể Student và đối tượng add của thực thể StudentAddress của nó vào cơ sở dữ liệu.

Sau đó, khi xóa đối tượng stud và gọi phương thức SaveChanges(), EF sẽ xóa stud cũng như bản ghi tương ứng của nó trong bảng StudentAddresses.

Do đó, EF được cho phép cascade delete theo mặc định .

Cascade Delete trong mối quan hệ một-nhiều

Hãy xem ví dụ sau đây: các thực thể StudentStandard có mối quan hệ một-nhiều.


public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual Standard Standard { get; set; }
}
       
public class Standard
{
    public Standard()
    {
        Students = new List();
    }
    public int StandardId { get; set; }
    public string Description { get; set; }

    public virtual ICollection Students { get; set; }
}

Ví dụ sau đây minh họa cascade delete giữa những thực thể có mối quan hệ một-nhiều :


using (var ctx = new SchoolContext()) 
{

    var student1 = new Student() { StudentName = "James" };
    var student2 = new Student() { StudentName = "Gandhi" };

    var standard1 = new Standard() { StandardName = "Standard 1" };

    student1.Standard = standard1;
    student2.Standard = standard1;

    ctx.Students.Add(student1);
    ctx.Students.Add(student2);
                
    //inserts students and standard1 into db
    ctx.SaveChanges();

    //deletes standard1 from db and also set standard_StandardId FK column in Students table to null for
    // all the students that reference standard1.
    ctx.Standards.Remove(standard1);

    ctx.SaveChanges();
}

Trong ví dụ trên, EF xóa đối tượng standard1 khỏi cơ sở dữ liệu và nó cũng thiết lập cột khóa ngoại standard_StandardId trong bảng Students thành null cho tất cả các bản ghi tham chiếu đối tượng standard1.

Lưu ý: EF tự động xóa các bản ghi liên quan trong bảng ở giữa cho các thực thể có mối quan hệ nhiều-nhiều nếu một thực thể bị xóa.

Do đó, EF được cho phép cascade delete mặc định cho toàn bộ những thực thể .

Tắt Cascade Delete

Sử dụng Fluent API để cấu hình tắt cascade delete cho các thực thể để bằng phương thức WillCascadeOnDelete(), như ví dụ bên dưới.


public class SchoolContext<: DbContext
{
    public SchoolContext():base("MySchool")
    {
    }

    public DbSet Students { get; set; }
    public DbSet Standards { get; set; }
        
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity()
            .HasOptional(s => s.Standard)
            .WithMany()
            .WillCascadeOnDelete(false);
    }
}

Lưu ý: Không có attribute chú thích dữ liệu nào có sẵn để tắt cascade delete.

Sử dụng Stored Procedures trong Code First

Entity Framework 6 Code First cung cấp khả năng tạo và sử dụng stored procedure để thực hiện các thao tác thêm, cập nhật và xóa khi gọi phương thức SaveChanges().

Chúng ta hãy sử dụng các stored procedure cho các thao tác CUD (Create, Update, Delete) cho thực thể Student sau.


class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
    public DateTime DoB { get; set; }
}

Sử dụng phương thức MapToStoredProcedures() để ánh xạ một thực thể với các stored procedure mặc định (các stored procedure mặc định này sẽ được tạo bởi EF API). Ví dụ sau ánh xạ thực thể Student với các stored procedure mặc định.


public class SchoolContext: DbContext 
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity()
                    .MapToStoredProcedures();
    }

    public DbSet Students { get; set; }
}

EF API sẽ tạo ra ba stored procedure Student_Insert, Student_UpdateStudent_Delete cho thực thể Student ở trên, như hình dưới đây.

Sử dụng Stored Procedures trong Code First

Các stored procedure Student_InsertStudent_Update có các tham số cho tất cả các thuộc tính của thực thể Student. Riêng stored procedure Student_Delete chỉ có một tham số cho thuộc tính khóa chính StudentId của thực thể Student. Sau đây là mã của các stored procedure.


CREATE PROCEDURE [dbo].[Student_Insert]
    @StudentName [nvarchar](max),
    @DoB [datetime]
AS
BEGIN
    INSERT [dbo].[Students]([StudentName], [DoB])
    VALUES (@StudentName, @DoB)
    
    DECLARE @StudentId int
    SELECT @StudentId = [StudentId]
    FROM [dbo].[Students]
    WHERE @@ROWCOUNT > 0 AND [StudentId] = scope_identity()
    
    SELECT t0.[StudentId]
    FROM [dbo].[Students] AS t0
    WHERE @@ROWCOUNT > 0 AND t0.[StudentId] = @StudentId
END

CREATE PROCEDURE [dbo].[Student_Update]
    @StudentId [int],
    @StudentName [nvarchar](max),
    @DoB [datetime]
AS
BEGIN
    UPDATE [dbo].[Students]
    SET [StudentName] = @StudentName, [DoB] = @DoB
    WHERE ([StudentId] = @StudentId)
END

CREATE PROCEDURE [dbo].[Student_Delete]
    @StudentId [int]
AS
BEGIN
    DELETE [dbo].[Students]
    WHERE ([StudentId] = @StudentId)
END

Ánh xạ Stored procedure tùy chỉnh cho một thực thể

EF 6 được cho phép bạn sử dụng những stored procedure tùy chỉnh của riêng bạn và ánh xạ chúng tới một thực thể. Bạn cũng hoàn toàn có thể thông số kỹ thuật ánh xạ tham số với những thuộc tính của thực thể .

Ví dụ sau ánh xạ các stored procedure tùy chỉnh với thực thể Student.


protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity()
		.MapToStoredProcedures(p => p.Insert(sp => sp.HasName("sp_InsertStudent").Parameter(pm => pm.StudentName, "name").Result(rs => rs.StudentId, "Id"))
			.Update(sp => sp.HasName("sp_UpdateStudent").Parameter(pm => pm.StudentName, "name"))
			.Delete(sp => sp.HasName("sp_DeleteStudent").Parameter(pm => pm.StudentId, "Id"))
            );
}

Trong ví dụ trên, thực thể Student được ánh xạ tới các stored procedure sp_InsertStudent, sp_UpdateStudentsp_DeleteStudent. Nó cũng cấu hình ánh xạ giữa các tham số và thuộc tính của thực thể.

Sử dụng stored procedure cho tất cả các thực thể

Bạn hoàn toàn có thể ánh xạ toàn bộ những thực thể của mình với những stored procedure mặc định trong một câu lệnh như dưới đây .


protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Types().Configure(t => t.MapToStoredProcedures());
}

Hạn chế

  • Chỉ có thể sử dụng Fluent API để ánh xạ các stored procedure. Không có attribute chú thích dữ liệu nào có sẵn trong EF 6 để ánh xạ stored procedure.
  • Bạn phải ánh xạ các stored procedure thêm, cập nhật và xóa cho một thực thể nếu bạn muốn sử dụng stored procedure cho các hoạt động CUD. Ánh xạ chỉ một trong số các thao tác CUD là không được phép.

Chuyển cấu hình Fluent API sang một lớp riêng trong Code First

Như bạn đã thấy trong các hướng dẫn trước, chúng tôi đã cấu hình tất cả các lớp thực thể bằng Fluent API trong phương thức OnModelCreating().

Tuy nhiên, nó trở nên khó duy trì nếu bạn cấu hình một số lượng lớn các lớp thực thể trong OnModelCreating.

EF 6 được cho phép bạn tạo một lớp riêng cho từng thực thể và đặt toàn bộ những thông số kỹ thuật tương quan đến thực thể trong lớp đó .

Hãy xem ví dụ sau nơi chúng ta cấu hình thực thể Student.


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

    public DbSet Students { get; set; }
        
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity().ToTable("StudentInfo");
            
        modelBuilder.Entity().HasKey(s => s.StudentKey);
            
        modelBuilder.Entity()
            .Property(p => p.DateOfBirth)
            .HasColumnName("DoB")
            .HasColumnOrder(3)
            .HasColumnType("datetime2");

        modelBuilder.Entity()
            .Property(p => p.StudentName)
            .HasMaxLength(50);
                    
        modelBuilder.Entity()
            .Property(p => p.StudentName)
            .IsConcurrencyToken();
            
        modelBuilder.Entity()
            .HasMany(s => s.Courses)
            .WithMany(c => c.Students)
            .Map(cs =>
            {
                cs.MapLeftKey("StudentId");
                cs.MapRightKey("CourseId");
                cs.ToTable("StudentCourse");
            });
    }
}

Bây giờ, bạn có thể di chuyển tất cả các cấu hình liên quan đến thực thể Student sang một lớp riêng có nguồn gốc từ EntityTypeConfiguration.

Hãy xem lớp StudentEntityConfigurations sau bao gồm tất cả các cấu hình cho thực thể Student.


public class StudentEntityConfiguration: EntityTypeConfiguration
{
    public StudentEntityConfiguration()
    {
            this.ToTable("StudentInfo");
                
            this.HasKey(s => s.StudentKey);
                
            this.Property(p => p.DateOfBirth)
                .HasColumnName("DoB")
                .HasColumnOrder(3)
                .HasColumnType("datetime2");

            this.Property(p => p.StudentName)
                .HasMaxLength(50);
                        
            this.Property(p => p.StudentName)
                .IsConcurrencyToken();
                
            this.HasMany(s => s.Courses)
                .WithMany(c => c.Students)
                .Map(cs =>
                {
                    cs.MapLeftKey("StudentId");
                    cs.MapRightKey("CourseId");
                    cs.ToTable("StudentCourse");
                });
    }
}

Như bạn có thể thấy ở trên, chúng tôi đã chuyển tất cả các cấu hình cho thực thể Student vào phương thức khởi tạo của lớp StudentEntityConfiguration, có nguồn gốc từ EntityTypeConfiguration.

Bây giờ, bạn cần thêm lớp thông số kỹ thuật tùy chỉnh này sử dụng Fluent API, như ví dụ bên dưới .


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

    public DbSet Students { get; set; }
        
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Moved all Student related configuration to StudentEntityConfiguration class
        modelBuilder.Configurations.Add(new StudentEntityConfiguration());
    }
}

Do đó, bạn hoàn toàn có thể sử dụng nhiều lớp thông số kỹ thuật để tăng năng lực đọc và bảo dưỡng .

Seed Data trong Code-First

Bạn hoàn toàn có thể chèn tài liệu vào những bảng cơ sở tài liệu của mình trong quy trình khởi tạo cơ sở tài liệu. Điều này sẽ rất quan trọng nếu bạn muốn cung ứng 1 số ít tài liệu thử nghiệm cho ứng dụng của bạn hoặc 1 số ít tài liệu chính mặc định cho ứng dụng của bạn .Để tạo tài liệu vào cơ sở tài liệu của bạn, bạn phải tạo một trình khởi tạo DB tùy chỉnh, như bạn đã tạo trong chương Khởi tạo cơ sở tài liệu và ghi đè phương pháp Seed .Ví dụ sau đây cho thấy cách bạn hoàn toàn có thể cung ứng tài liệu mặc định cho bảng Standard trong khi khởi tạo cơ sở tài liệu SchoolDB :


public class SchoolDBInitializer : DropCreateDatabaseAlways
{
    protected override void Seed(SchoolDBContext context)
    {
        IList defaultStandards = new List();

        defaultStandards.Add(new Standard() { StandardName = "Standard 1", Description = "First Standard" });
        defaultStandards.Add(new Standard() { StandardName = "Standard 2", Description = "Second Standard" });
        defaultStandards.Add(new Standard() { StandardName = "Standard 3", Description = "Third Standard" });

        context.Standards.AddRange(defaultStandards);

        base.Seed(context);
    }
}

Bây giờ, thiết lập lớp khởi tạo DB này trong lớp Context như bên dưới .


public class SchoolContext: DbContext 
{
    public SchoolContext(): base("SchoolDB") 
    {
        Database.SetInitializer(new SchoolDBInitializer());
    }
    
    public DbSet Students { get; set; }
    public DbSet Standards { get; set; }
}

Tạo các quy ước tùy chỉnh trong Code First

Bạn đã tìm hiểu và khám phá về những quy ước trong Code First trong phần đầu của bài viết. EF 6 cũng phân phối năng lực định nghĩa những quy ước tùy chỉnh của riêng bạn .Có hai loại quy ước chính : Quy ước thông số kỹ thuật và Quy ước quy mô .

Quy ước cấu hình

Quy ước thông số kỹ thuật là một cách để thông số kỹ thuật những thực thể mà không ghi đè thông số kỹ thuật mặc định được phân phối trong Fluent API .

Bạn có thể định nghĩa một quy ước cấu hình trong phương thức OnModelCreating() và cả trong lớp tùy chỉnh, tương tự như cách bạn sẽ định nghĩa ánh xạ thực thể thông thường với Fluent API.

Ví dụ: bạn muốn cấu hình một thuộc tính làm thuộc tính khóa có tên khớp với {tên thực thể}_ID, ví dụ: thuộc tính Student_ID của thực thể Student sẽ là khóa chính. Sau đây định nghĩa quy ước này.


protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder
        .Properties()
        .Where(p => p.Name == p.DeclaringType.Name + "_ID")
        .Configure(p => p.IsKey());

    base.OnModelCreating(modelBuilder);
}

Theo cùng một cách, bạn hoàn toàn có thể định nghĩa quy ước cho size của kiểu tài liệu .

Ví dụ dưới đây định nghĩa một quy ước cho các thuộc tính chuỗi. Nó sẽ tạo các cột nvarchar có kích thước 50 trong SQL Server cho tất cả các thuộc tính kiểu chuỗi của một thực thể.


protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
	modelBuilder
		.Properties()
		.Where(p => p.PropertyType.Name == "String")
		.Configure(p => p.HasMaxLength(50));

    base.OnModelCreating(modelBuilder);
}

Bạn cũng có thể định nghĩa một lớp tùy chỉnh cho quy ước này bằng cách kế thừa lớp Convention, như ví dụ bên dưới:


public class PKConvention : Convention
{
    public PKConvention()
    {
        this.Properties()
            .Where(p => p.Name == p.DeclaringType.Name + "_ID")
            .Configure(p => p.IsKey());
    }
}

Sau khi tạo lớp quy ước tùy chỉnh, hãy thêm nó vào những quy ước như dưới đây :


protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add();
}

Quy ước mô hình

Quy ước quy mô dựa trên siêu dữ liệu quy mô cơ bản. Có những quy ước cho cả CSDL và SSDL .

Tạo một lớp triển khai interface IConceptualModelConvention từ các quy ước CSDL và IStoreModelConvention từ các quy ước SSDL.

Truy cập Quy ước tùy chỉnh trong EF 6 để biết thêm thông tin .

Nếu Comdy hữu ích và giúp bạn tiết kiệm thời gian

Bạn hoàn toàn có thể vui mắt tắt trình chặn quảng cáo ❤ ️ để tương hỗ chúng tôi duy trì hoạt động giải trí của website .

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *