Entity Framework Core toàn tập

Entity Framework Core là phiên bản mới của Entity Framework sau EF 6.x. Nó là mã nguồn mở, nhẹ, có thể mở rộng và là phiên bản đa nền tảng của công nghệ truy cập dữ liệu Entity Framework.

Entity Framework là một framework Object/Relational Mapping (O/RM - ánh xạ quan hệ/đối tượng). Đây là một cải tiến của ADO.NET, cung cấp cho các nhà phát triển một cơ chế tự động để truy cập và lưu trữ dữ liệu trong cơ sở dữ liệu.

EF Core được dự định sẽ sử dụng với các ứng dụng .NET Core. Tuy nhiên, nó cũng có thể được sử dụng với các ứng dụng dựa trên .NET Framework 4.5+ tiêu chuẩn.

Hình dưới đây minh họa các loại ứng dụng được hỗ trợ, .NET Framework và HĐH.

Entity Framework Core

Lịch sử phiên bản Entity Framework Core

Phiên bản EF Core Ngày phát hành
Entity Framework Core 5.0 Tháng 11 - 2020 (dự kiến)
Entity Framework Core 3.1 Tháng 12 - 2019
Entity Framework Core 3.0 Tháng 9 - 2019
Entity Framework Core 2.2 Tháng 12 - 2018
Entity Framework Core 2.1 Tháng 5 - 2018
Entity Framework Core 2.0 Tháng 8 - 2017
Entity Framework Core 1.1 Tháng 11 - 2016
Entity Framework Core 1.0 Tháng 6 - 2016

Mã nguồn Entity Framework Core trên GitHub: https://github.com/aspnet/EntityFrameworkCore

Lộ trình phát triển Entity Framework Core: docs.microsoft.com/en-us/ef/core/what-is-new/roadmap

Theo dõi các vấn đề của Entity Framework Core tại: https://github.com/aspnet/EntityFrameworkCore/issues

Tài liệu chính thức của Entity Framework Core: https://docs.microsoft.com/ef/core

Cách tiếp cận phát triển của EF Core

EF Core hỗ trợ hai cách tiếp cận phát triển: Code First và Database First. EF Core chủ yếu nhắm vào cách tiếp cận Code First và cung cấp ít hỗ trợ cho cách tiếp cận Database First vì trình thiết kế trực quan hoặc trình hướng dẫn cho mô hình DB không được hỗ trợ kể từ EF Core 2.0.

Theo cách tiếp cận Code First, EF Core API tạo cơ sở dữ liệu và các bảng bằng cách sử dụng chuyển đổi (migration) dựa trên các quy ước và cấu hình được cung cấp trong các lớp thực thể của bạn. Cách tiếp cận này rất hữu ích trong thiết kế hướng miền (Domain Driven Design - DDD).

Theo cách tiếp cận Database First, EF Core API tạo các lớp thực thể và Context dựa trên cơ sở dữ liệu hiện có của bạn bằng cách sử dụng các lệnh của EF Core. Điều này được hỗ trợ hạn chế trong EF Core vì nó không hỗ trợ trình thiết kế trực quan hoặc trình hướng dẫn.

Cách tiếp cận phát triển của EF Core

EF Core với EF 6

Entity Framework Core là phiên bản mới và cải tiến của Entity Framework cho các ứng dụng .NET Core. EF Core là phiên bản mới hoàn toàn, vì vậy nó vẫn chưa đầy đủ như EF 6.

EF Core tiếp tục hỗ trợ các tính năng và khái niệm giống như EF 6 như sau:

  1. DbContext và Dbset.
  2. Mô hình dữ liệu.
  3. Truy vấn bằng cách sử dụng Linq-to-Entities.
  4. Theo dõi thay đổi.
  5. SaveChanges.
  6. Chuyển đổi (Migration).

EF Core dần dần sẽ có hầu hết các tính năng của EF 6. Tuy nhiên, có một số tính năng của EF 6 không được hỗ trợ trong EF Core 2.0, chẳng hạn như:

  1. EDMX/Graphical Visualization của mô hình.
  2. Trình hướng dẫn mô hình dữ liệu thực thể (cho phương pháp Database First).
  3. ObjectContext API.
  4. Truy vấn bằng cách sử dụng Entity SQL.
  5. Chuyển đổi tự động.
  6. Kế thừa: Bảng trên mỗi kiểu (TPT)
  7. Kế thừa: Bảng trên mỗi lớp con (TPC)
  8. Mối quan hệ nhiều-nhiều.
  9. Chia tách thực thể.
  10. Kiểu dữ liệu không gian.
  11. Ánh xạ stored procedure với DbContext cho hoạt động CUD (Create, Update, Delete).
  12. Seed data.

EF Core có các tính năng mới sau đây không được hỗ trợ trong EF 6.x:

  1. Cấu hình mối quan hệ dễ dàng
  2. Các thao tác INSERT, UPDATE và DELETE hàng loạt.
  3. Trình cung cấp trong bộ nhớ để thử nghiệm.
  4. Hỗ trợ cho IoC (Đảo ngược sự phụ thuộc).
  5. Ràng buộc duy nhất (unique contraints).
  6. Thuộc tính ẩn (shadow property).
  7. Khóa thay thế.
  8. Bộ lọc truy vấn toàn cục.
  9. Ánh xạ trường.
  10. DbContext chung
  11. Các mẫu tốt hơn để xử lý các biểu đồ thực thể bị ngắt kết nối

Tìm hiểu thêm về sự khác biệt của EF Core và EF 6 tại đây.

Trình cung cấp cơ sở dữ liệu của EF Core

Entity Framework Core sử dụng mô hình trình cung cấp để truy cập nhiều loại cơ sở dữ liệu khác nhau. EF Core có các trình cung cấp dưới dạng các gói NuGet mà bạn cần cài đặt.

Bảng sau liệt kê một số trình cung cấp cơ sở dữ liệu và các gói NuGet cho EF Core.

Cài đặt Entity Framework Core

Entity Framework Core có thể được sử dụng với các ứng dụng dựa trên .NET Core hoặc .NET Framework 4.6. Ở phần này, bạn sẽ tìm hiểu cách cài đặt và sử dụng Entity Framework Core 2.0 trong các ứng dụng .NET Core bằng Visual Studio 2017.

EF Core không phải là một phần của .NET Core và .NET Framework tiêu chuẩn. Nó là một gói NuGet. Bạn cần cài đặt các gói NuGet cho hai thành phần sau đây để sử dụng EF Core trong ứng dụng của bạn:

  1. Trình cung cấp cơ sở dữ liệu của EF Core.
  2. Công cụ EF Core.

Cài đặt trình cung cấp cơ sở dữ liệu của EF Core

Như đã đề cập trong phần trước, EF Core cho phép chúng ta truy cập cơ sở dữ liệu thông qua mô hình trình cung cấp.

Có các trình cung cấp cơ sở dữ liệu EF Core cho các cơ sở dữ liệu khác nhau. Các trình cung cấp này có sẵn dưới dạng các gói NuGet.

Trước tiên, chúng ta cần cài đặt gói NuGet cho trình cung cấp cơ sở dữ liệu mà chúng ta muốn truy cập. Ở đây, chúng tôi muốn truy cập cơ sở dữ liệu MS SQL Server, vì vậy chúng tôi cần cài đặt gói NuGet Microsoft.EntityFrameworkCore.SqlServer.

Để cài đặt gói NuGet của trình cung cấp DB, bạn nhấp chuột phải vào dự án trong Solution Explorer trong Visual Studio và chọn Manage NuGet Packages.. (hoặc chọn trên menu: Tools -> NuGet Package Manager -> Manage NuGet Packages For Solution).

Cài đặt Entity Framework Core

Điều này sẽ mở giao diện trình quản lý gói NuGet. Nhấp vào tab Browse hoặc tab Updates và tìm kiếm Microsoft.entityframeworkcore trong hộp tìm kiếm ở góc trên cùng bên trái, như hình ảnh bên dưới.

Cài đặt Entity Framework Core

Chọn trình cung cấp cho cơ sở dữ liệu bạn muốn truy cập. Trong trường hợp này, chọn Microsoft.EntityFrameworkCore.SqlServer cho MS SQL Server như hình trên. (đảm bảo rằng nó có logo .NET và tác giả là Microsoft). Nhấp vào Install để bắt đầu cài đặt.

Cửa sổ xem trước hiển thị danh sách các gói sẽ cài đặt trong ứng dụng của bạn. Xem lại các thay đổi và nhấp vào OK.

Cài đặt Entity Framework Core

Cuối cùng, chấp nhận các điều khoản cấp phép liên quan đến các gói sẽ được cài đặt.

Cài đặt Entity Framework Core

Điều này sẽ cài đặt gói Microsoft.EntityFrameworkCore.SqlServer. Kiểm tra nó trong Dependencies -> NuGet, như hình ảnh bên dưới.

Cài đặt Entity Framework Core
Lưu ý: gói NuGet của trình cung cấp cũng đã cài đặt các gói phụ thuộc khác như Microsoft.EntityFrameworkCore.RelationalSystem.Data.SqlClient.

Ngoài ra, bạn cũng có thể cài đặt gói NuGet của trình cung cấp bằng Package Manager Console. Chuyển đến Tools -> NuGet Package Manager -> Package Manager Console và thực hiện lệnh sau để cài đặt gói trình cung cấp SQL Server:


PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer

Cài đặt công cụ của EF Core

Cùng với gói trình cung cấp DB, bạn cũng cần cài đặt các công cụ EF để thực thi các lệnh EF Core.

Điều này giúp dễ dàng thực hiện một số nhiệm vụ liên quan đến EF Core trong dự án của bạn tại thời điểm thiết kế, chẳng hạn như chuyển đổi, scaffolding, v.v.

Công cụ EF có sẵn dưới dạng các gói NuGet. Bạn có thể cài đặt gói NuGet cho các công cụ EF tùy thuộc vào nơi bạn muốn thực thi các lệnh: sử dụng Package Manager Console (phiên bản PowerShell của các lệnh EF Core) hoặc sử dụng dotnet CLI.

Cài đặt công cụ EF Core cho PMC

Để thực thi các lệnh EF Core từ Package Manager Console, hãy tìm kiếm gói Microsoft.EntityFrameworkCore.Tools từ NuGet UI và cài đặt nó như hình ảnh bên dưới.

Cài đặt công cụ của EF Core

Điều này sẽ cho phép bạn thực thi các lệnh EF Core cho scaffolding, chuyển đổi, vv trực tiếp từ Package Manager Console (PMC) trong Visual Studio.

Cài đặt công cụ EF Core cho dotnet CLI

Nếu bạn muốn thực thi các lệnh EF Core từ CLI (Giao diện dòng lệnh) của .NET Core, trước tiên hãy cài đặt gói NuGet Microsoft.EntityFrameworkCore.Tools.DotNet bằng NuGet UI.

Sau khi cài đặt gói Microsoft.EntityFrameworkCore.Tools.DotNet, chỉnh sửa file .csproj bằng cách nhấp chuột phải vào dự án trong Solution Explorer và chọn Edit <projectname>.csproj. Thêm node <DotNetCliToolReference> như ví dụ dưới đây. Đây là một bước bổ sung mà bạn cần thực hiện để thực thi các lệnh EF Core 2.0 từ dotnet CLI trong Visual Studio 2017.


<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
  </ItemGroup>
</Project>

Bây giờ, hãy mở dấu nhắc lệnh (hoặc thiết bị đầu cuối như PowerShell chẳng hạn) từ thư mục gốc của dự án của bạn và thực hiện các lệnh EF Core từ CLI bắt đầu bằng lệnh dotnet ef, như hình ảnh bên dưới.

Cài đặt công cụ EF Core cho dotnet CLI

Tạo mô hình từ database có sẵn trong EF Core

Ở phần này bạn sẽ tìm hiểu cách tạo lớp Context và các lớp thực thể cho cơ sở dữ liệu có sẵn trong Entity Framework Core. Tạo các lớp thực thể và Context cho cơ sở dữ liệu có sẵn được gọi là phương pháp tiếp cận Database First.

EF Core không hỗ trợ trình thiết kế trực quan cho mô hình DB và trình hướng dẫn để tạo các thực thể và các lớp Context tương tự như EF 6.

Vì vậy, chúng ta cần thực hiện kỹ thuật đảo ngược bằng cách sử dụng lệnh Scaffold-DbContext. Lệnh đảo ngược này tạo ra các lớp thực thể và Context (bằng cách kế thừa từ lớp DbContext) dựa trên lược đồ của cơ sở dữ liệu hiện có.

Chúng ta sẽ tạo các lớp thực thể và Context cho cơ sở dữ liệu SchoolDB sau trong MS SQL Server như hình ảnh bên dưới.

Tạo mô hình từ database có sẵn trong EF Core

Lệnh Scaffold-DbContext

Sử dụng lệnh Scaffold-DbContext để tạo một mô hình dựa trên cơ sở dữ liệu hiện tại của bạn. Các tham số sau có thể được chỉ định cho Scaffold-DbContext trong Package Manager Console:

Scaffold-DbContext [-Connection] [-Provider] [-OutputDir] [-Context] [-Schemas>] [-Tables>] 
                   [-DataAnnotations] [-Force] [-Project] [-StartupProject] [<CommonParameters>]

Trong Visual Studio, chọn menu Tools -> NuGet Package Manger -> Package Manger Console và chạy lệnh sau:

PM> Scaffold-DbContext "Server=.\SQLExpress;Database=SchoolDB;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

Trong lệnh trên, tham số đầu tiên là một chuỗi kết nối bao gồm ba phần: Máy chủ DB, tên cơ sở dữ liệu và thông tin bảo mật.

Ở đây, Server=.\SQLExpress; đề cập đến máy chủ cơ sở dữ liệu SQLEXPRESS cục bộ.  Database=SchoolDB; chỉ định tên cơ sở dữ liệu "SchoolDB" mà chúng ta sẽ tạo các lớp. Trusted_Connection=True; chỉ định xác thực Windows. Nó sẽ sử dụng thông tin đăng nhập Windows để kết nối với SQL Server.

Tham số thứ hai là tên nhà cung cấp. Chúng tôi sử dụng nhà cung cấp cho SQL Server, vì vậy nó là Microsoft.EntityFrameworkCore.SqlServer.

Tham số -OutputDir mô tả thư mục nơi mà chúng tôi muốn tạo ra tất cả các lớp thực thể, đó là thư mục Models.

Sử dụng lệnh sau để xem chi tiết về lệnh Scaffold-DbContext:

PM> get-help scaffold-dbcontext –detailed

Lệnh trên Scaffold-DbContext tạo các lớp thực thể cho mỗi bảng trong cơ sở dữ liệu SchoolDB và lớp Context với các cấu hình Fluent API cho tất cả các thực thể trong thư mục Models.

Tạo mô hình từ database có sẵn trong EF Core

Sau đây là lớp thực thể Student được tạo cho bảng Student.


using System;
using System.Collections.Generic;

namespace EFCoreTutorials.Models
{
    public partial class Student
    {
        public Student()
        {
            StudentCourse = new HashSet<StudentCourse>();
        }

        public int StudentId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int? StandardId { get; set; }

        public Standard Standard { get; set; }
        public StudentAddress StudentAddress { get; set; }
        public ICollection<StudentCourse> StudentCourse { get; set; }
    }
}

Sau đây là lớp SchoolDBContext mà bạn có thể sử dụng để lưu hoặc truy xuất dữ liệu.


using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

namespace EFCoreTutorials.Models
{
    public partial class SchoolDBContext : DbContext
    {
        public virtual DbSet<Course> Course { get; set; }
        public virtual DbSet<Standard> Standard { get; set; }
        public virtual DbSet<Student> Student { get; set; }
        public virtual DbSet<StudentAddress> StudentAddress { get; set; }
        public virtual DbSet<StudentCourse> StudentCourse { get; set; }
        public virtual DbSet<Teacher> Teacher { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
                optionsBuilder.UseSqlServer(@"Server=.\SQLExpress;Database=SchoolDB;Trusted_Connection=True;");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>(entity =>
            {
                entity.Property(e => e.CourseName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Teacher)
                    .WithMany(p => p.Course)
                    .HasForeignKey(d => d.TeacherId)
                    .OnDelete(DeleteBehavior.Cascade)
                    .HasConstraintName("FK_Course_Teacher");
            });

            modelBuilder.Entity<Standard>(entity =>
            {
                entity.Property(e => e.Description)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.StandardName)
                    .HasMaxLength(50)
                    .IsUnicode(false);
            });

            modelBuilder.Entity<Student>(entity =>
            {
                entity.Property(e => e.StudentId).HasColumnName("StudentID");

                entity.Property(e => e.FirstName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.LastName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Standard)
                    .WithMany(p => p.Student)
                    .HasForeignKey(d => d.StandardId)
                    .OnDelete(DeleteBehavior.Cascade)
                    .HasConstraintName("FK_Student_Standard");
            });

            modelBuilder.Entity<StudentAddress>(entity =>
            {
                entity.HasKey(e => e.StudentId);

                entity.Property(e => e.StudentId)
                    .HasColumnName("StudentID")
                    .ValueGeneratedNever();

                entity.Property(e => e.Address1)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.Address2)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.City)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.State)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Student)
                    .WithOne(p => p.StudentAddress)
                    .HasForeignKey<StudentAddress>(d => d.StudentId)
                    .HasConstraintName("FK_StudentAddress_Student");
            });

            modelBuilder.Entity<StudentCourse>(entity =>
            {
                entity.HasKey(e => new { e.StudentId, e.CourseId });

                entity.HasOne(d => d.Course)
                    .WithMany(p => p.StudentCourse)
                    .HasForeignKey(d => d.CourseId)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("FK_StudentCourse_Course");

                entity.HasOne(d => d.Student)
                    .WithMany(p => p.StudentCourse)
                    .HasForeignKey(d => d.StudentId)
                    .HasConstraintName("FK_StudentCourse_Student");
            });

            modelBuilder.Entity<Teacher>(entity =>
            {
                entity.Property(e => e.StandardId).HasDefaultValueSql("((0))");

                entity.Property(e => e.TeacherName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Standard)
                    .WithMany(p => p.Teacher)
                    .HasForeignKey(d => d.StandardId)
                    .OnDelete(DeleteBehavior.Cascade)
                    .HasConstraintName("FK_Teacher_Standard");
            });
        }
    }
}
Lưu ý: EF Core chỉ tạo các lớp thực thể cho các bảng chứ không tạo cho Stored Procedure hoặc View.

DotNet CLI

Nếu bạn sử dụng giao diện dòng lệnh dotnet để thực thi các lệnh EF Core thì hãy mở dấu nhắc lệnh và điều hướng đến thư mục gốc của dự án và thực hiện lệnh dotnet ef dbcontext scaffold sau:

> dotnet ef dbcontext scaffold "Server=.\SQLEXPRESS;Database=SchoolDB;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models

Như vậy là bạn có thể tạo mô hình EF Core cho cơ sở dữ liệu có sẵn.

Lưu ý: Khi bạn đã tạo mô hình, bạn phải sử dụng các lệnh chuyển đổi (migration) bất cứ lúc nào bạn thay đổi mô hình để giữ cho cơ sở dữ liệu được cập nhật với mô hình.

DbContext trong Entity Framework Core

Lớp DbContext là một phần không thể thiếu của Entity Framework. Một thể hiện của DbContext đại diện cho một phiên làm việc với cơ sở dữ liệu có thể được sử dụng để truy vấn và lưu các thể hiện của các thực thể của bạn vào cơ sở dữ liệu.

DbContext là sự kết hợp của các mẫu Unit Of Work và Repository. DbContext trong EF Core cho phép chúng ta thực hiện các tác vụ sau:

  1. Quản lý kết nối cơ sở dữ liệu.
  2. Cấu hình mô hình và mối quan hệ.
  3. Truy vấn cơ sở dữ liệu.
  4. Lưu dữ liệu vào cơ sở dữ liệu.
  5. Cấu hình theo dõi thay đổi.
  6. Bộ nhớ đệm.
  7. Quản lý giao dịch.

Để sử dụng DbContext trong ứng dụng, chúng ta cần tạo lớp kế thừa từ lớp DbContext, còn được gọi là lớp Context.

Lớp Context này thường bao gồm các thuộc tính Dbset <TEntity> cho mỗi thực thể trong mô hình. Hãy xem ví dụ sau mình họa về lớp Context trong EF Core.


public class SchoolContext : DbContext
{
    public SchoolContext()
    {
  
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    }
    //entities
    public DbSet<Student> Students { get; set; }
    public DbSet<Course> Courses { get; set; }
} 

Trong ví dụ trên, lớp SchoolContext có nguồn gốc từ lớp DbContext và chứa các  thuộc tính DbSet<TEntity> của các lớp thực thể StudentCourse. Nó cũng ghi đè hai phương thức OnConfiguringOnModelCreating.

Chúng ta phải tạo một thể hiện của SchoolContext để kết nối với cơ sở dữ liệu và lưu hoặc truy xuất dữ liệu Student hoặc Course.

Phương thức OnConfiguring() cho phép chúng ta lựa chọn và cấu hình nguồn dữ liệu được sử dụng với một Context sử dụng DbContextOptionsBuilder. Tìm hiểu cách cấu hình một lớp DbContext tại đây .

Phương thức OnModelCreating() cho phép chúng ta cấu hình mô hình sử dụng ModelBuilder Fluent API.

Các phương thức của DbContext

Phương thức Mô tả
Add Thêm một thực thể mới vào DbContext với trạng thái Added và bắt đầu theo dõi nó. Dữ liệu thực thể mới này sẽ được chèn vào cơ sở dữ liệu khi gọi SaveChanges().
AddAsync Phương thức không đồng bộ để thêm một thực thể mới vào DbContext với trạng thái Added và bắt đầu theo dõi nó. Dữ liệu thực thể mới này sẽ được chèn vào cơ sở dữ liệu khi gọi SaveChangesAsync().
AddRange Thêm một tập các thực thể mới vào DbContext với trạng thái Added và bắt đầu theo dõi nó. Dữ liệu các thực thể mới này sẽ được chèn vào cơ sở dữ liệu khi gọi SaveChanges().
AddRangeAsync Phương thức không đồng bộ để thêm tập các thực thể mới vào DbContext với trạng thái Added và bắt đầu theo dõi nó. Dữ liệu thực thể mới này sẽ được chèn vào cơ sở dữ liệu khi gọi SaveChangesAsync().
Attach Đính kèm một thực thể mới hoặc đã tồn tại vào DbContext với trạng thái Unchanged và bắt đầu theo dõi nó.
AttachRange Đính kèm một tập các thực thể mới hoặc đã tồn tại vào DbContext với trạng thái Unchanged và bắt đầu theo dõi nó.
Entry Trả về một EntityEntry cho thực thể. Entry cung cấp quyền truy cập để thay đổi thông tin theo dõi và hoạt động cho thực thể.
Find Tìm một thực thể với các giá trị khóa chính đã cho.
FindAsync Phương thức không đồng bộ để tìm một thực thể với các giá trị khóa chính đã cho.
Remove Thiết lập trạng thái Deleted cho thực thể được chỉ định sẽ xóa dữ liệu khi gọi SaveChanges().
RemoveRange Thiết lập trạng thái Deleted cho một tập các thực thể sẽ xóa dữ liệu trong một lần gọi DB duy nhất khi gọi SaveChanges().
SaveChanges Thực thi lệnh INSERT, UPDATE hoặc DELETE vào cơ sở dữ liệu cho các thực thể với trạng thái Added, Modified hoặc Deleted.
SaveChangesAsync Phương thức SaveChanges() không đồng bộ
Set Tạo một DbSet<TEntity> được sử dụng để truy vấn và lưu các thể hiện của TEntity.
Update Đính kèm thực thể bị ngắt kết nối với trạng thái Modified và bắt đầu theo dõi nó. Dữ liệu sẽ được lưu khi gọi SaveChagnes().
UpdateRange Đính kèm một tập các thực thể bị ngắt kết nối với trạng thái Modified và bắt đầu theo dõi nó. Dữ liệu sẽ được lưu khi gọi SaveChagnes().
OnConfiguring Ghi đè phương thức này để cấu hình cơ sở dữ liệu (và các tùy chọn khác) sẽ được sử dụng cho Context này. Phương thức này được gọi khi mỗi thể hiện của Context được tạo.
OnModelCreating Ghi đè phương thức này để cấu hình cho các thực thể trong các thuộc tính DbSet<TEntity> của lớp Context.

Các thuộc tính của DbContext

Thuộc tính Mô tả
ChangeTracker Cung cấp truy cập vào thông tin và hoạt động cho các thể hiện của thực thể mà Context này đang theo dõi.
Database Cung cấp truy cập vào thông tin và hoạt động liên quan đến cơ sở dữ liệu cho Context này.
Model Trả về siêu dữ liệu của các thực thể, mối quan hệ giữa chúng và cách chúng ánh xạ tới cơ sở dữ liệu.

Tạo ứng dụng EF Core đầu tiên

Ở hướng dẫn này, bạn sẽ tìm hiểu từng bước cách sử dụng Entity Framework Core theo cách tiếp cận Code First. Để minh họa, chúng tôi sẽ tạo một ứng dụng .NET Core Console bằng Visual Studio 2017 (hoặc mới hơn).

Ứng dụng .NET Core Console có thể được tạo bằng Visual Studio 2017 hoặc Giao diện dòng lệnh (CLI) cho .NET Core. Ở đây chúng tôi sẽ sử dụng Visual Studio 2017.

Để tạo ứng dụng .NET Core Console, hãy mở Visual Studio 2017 và chọn trên menu: File -> New -> Project.. Điều này sẽ mở cửa sổ bật lên New Project như hình ảnh bên dưới.

Ứng dụng Entity Framework Core đầu tiên

Trong cửa sổ New Project, mở rộng Installed -> Visual C# ở khung bên trái và chọn mẫu Console App (.NET Core) trong ngăn giữa.

Nhập tên dự án, vị trí đặt dự án và nhấp vào nút OK để tạo ứng dụng bảng điều khiển, như hình ảnh bên dưới.

Ứng dụng Entity Framework Core đầu tiên

Bây giờ, chúng ta cần cài đặt EF Core trong ứng dụng của mình bằng Package Manager Console. Chọn trên menu: Tools -> NuGet Package Manager -> Package Manager Console và thực hiện lệnh sau để cài đặt gói nhà cung cấp SQL Server:

PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer

Tạo mô hình dữ liệu thực thể

Entity Framework cần có một mô hình dữ liệu thực thể để giao tiếp với cơ sở dữ liệu. Nó xây dựng một mô hình dựa trên các lớp thực thể của bạn bằng cách sử dụng cấu hình chú thích dữ liệu và Fluent API.

Mô hình EF bao gồm ba phần: mô hình khái niệm, mô hình lưu trữ và ánh xạ giữa mô hình khái niệm và mô hình lưu trữ.

Theo cách tiếp cận Code First, EF xây dựng mô hình khái niệm dựa trên các lớp thực thể của bạn, lớp Context và các cấu hình.

EF Core xây dựng mô hình lưu trữ và ánh xạ dựa trên trình cung cấp cơ sở dữ liệu bạn sử dụng. Ví dụ, mô hình lưu trữ của SQL Server sẽ khác với DB2.

EF sử dụng mô hình này cho các hoạt động CRUD (Create, Read, Update, Delete) cho cơ sở dữ liệu.

Tạo Migration trong EF Core

EF Core có các lệnh chuyển đổi (Migration) khác nhau để tạo hoặc cập nhật cơ sở dữ liệu dựa trên mô hình.

Tại thời điểm này chưa có cơ sở dữ liệu SchoolDB. Vì vậy, chúng ta cần tạo cơ sở dữ liệu từ mô hình (thực thể và Context) bằng cách thêm chuyển đổi.

Chúng ta có thể thực thi lệnh chuyển đổi bằng cách sử dụng Package Manger Console hoặc dotnet CLI (giao diện dòng lệnh).

Trong Visual Studio, mở NuGet Package Manager Console từ Tools -> NuGet Package Manager -> Package Manager Console và nhập lệnh sau:

PM> add-migration CreateSchoolDB

Nếu bạn sử dụng dotnet CLI, hãy nhập lệnh sau.

> dotnet ef migrations add CreateSchoolDB

Xem chi tiết hướng dẫn từng bước tạo ứng dụng Entity Framework Core đầu tiên tại bài viết này.

Ứng dụng Entity Framework Core đầu tiên | Comdy
Tạo ứng dụng .NET Core Console đầu tiên và cấu hình sử dụng Entity Framework Core.

Các quy ước trong Entity Framework Core

Các quy ước là các quy tắc mặc định bằng cách sử dụng Entity Framework để xây dựng một mô hình dựa trên các lớp thực thể của bạn.

Trong bài viết Ứng dụng EF Core đầu tiên, EF Core API tạo ra một lược đồ cơ sở dữ liệu dựa trên các lớp thực thể và lớp Context, không có bất kỳ cấu hình bổ sung nào vì các lớp thực thể đã tuân theo các quy ước.

Hãy xem các lớp thực thể và lớp Context sau để hiểu các quy ước mặc định.


public class Student
{
    public int StudentId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public byte[] Photo { get; set; }
    public decimal Height { get; set; }
    public float Weight { get; set; }

    public int GradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int Id { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public IList<Student> Students { get; set; }
}

public class SchoolContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    { 
        optionsBuilder.UseSqlServer(@"Server=.\SQLEXPRESS;Database=SchoolDB;Trusted_Connection=True;");
    }

    public DbSet<Student> Students { get; set; }
}

Chúng ta sẽ tìm hiểu các quy ước của EF Core và cách thức EF Core API tạo cơ sở dữ liệu cho các thực thể trên.

Lược đồ

Theo mặc định, EF Core sẽ tạo tất cả các đối tượng cơ sở dữ liệu trong lược đồ dbo.

Quy ước về lược đồ trong Entity Framework Core

Bảng

EF Core sẽ tạo các bảng cơ sở dữ liệu cho tất cả các thuộc tính DbSet<TEntity> trong lớp Context có cùng tên với thuộc tính.

Nó cũng sẽ tạo các bảng cho các thực thể không phải là thuộc tính DbSet nhưng có thể truy cập thông qua các thuộc tính tham chiếu trong các thực thể DbSet khác.

Đối với ví dụ trên, EF Core sẽ tạo bảng Students cho thuộc tính DbSet<Student> trong lớp SchoolContext và bảng Grade cho thuộc tính Grade trong lớp thực thể Student, mặc dù lớp SchoolContext không có thuộc tính DbSet<Grade>.

Quy ước về bảng trong Entity Framework Core

Cột

Theo mặc định, EF Core sẽ tạo các cột cho tất cả các thuộc tính của một lớp thực thể có cùng tên với thuộc tính.

Nó sử dụng các thuộc tính tham chiếu và thuộc tính tập hợp trong việc xây dựng mối quan hệ giữa các bảng tương ứng trong cơ sở dữ liệu.

Quy ước về cột trong Entity Framework Core

Kiểu dữ liệu cột

Kiểu dữ liệu cho các cột trong bảng cơ sở dữ liệu tùy thuộc vào cách nhà cung cấp cơ sở dữ liệu ánh xạ kiểu dữ liệu C# với kiểu dữ liệu của cơ sở dữ liệu đã chọn.

Bảng sau liệt kê ánh xạ giữa kiểu dữ liệu C# sang kiểu dữ liệu cột 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 có ánh xạ
sbyte Không có ánh xạ (ném ra exception)
object Không có ánh xạ

Cột Null

EF Core tạo các cột null cho tất cả các kiểu dữ liệu tham chiếu và các thuộc tính kiểu nguyên thủy nullable, ví dụ: string, Nullable<int>, decimal?.

Cột NotNull

EF Core tạo các cột NotNull trong cơ sở dữ liệu cho tất cả các thuộc tính khóa chính và các thuộc tính kiểu nguyên thủy, ví dụ: int, float, decimal, DateTime, v.v.

Khóa chính

EF Core sẽ tạo cột khóa chính cho thuộc tính có tên Id hoặc <Entity Class Name>Id (không phân biệt chữ hoa chữ thường).

Ví dụ, EF Core sẽ tạo một cột khóa chính trong bảng Students nếu lớp Student có một thuộc tính có tên id, ID, iD, Id, studentid, StudentId, STUDENTID hoặc sTUdentID.

Quy ước về khóa chính trong Entity Framework Core

Khóa ngoại

Theo quy ước khóa ngoại, EF Core API sẽ tạo một cột khóa ngoại cho mỗi thuộc tính điều hướng tham chiếu trong một thực thể với một trong các mẫu đặt tên sau.

  • <Reference Navigation Property Name>Id
  • <Reference Navigation Property Name><Principal Primary Key Property Name>

Trong ví dụ của chúng tôi (các thực thể StudentGrade), EF Core sẽ tạo một cột khóa ngoại GradeId trong bảng Students, như được mô tả trong hình dưới đây.

Quy ước về khóa ngoại trong Entity Framework Core

Bảng sau liệt kê các tên cột khóa ngoại cho các tên thuộc tính tham chiếu khác và các tên thuộc tính khóa chính.

Tên thuộc tính tham chiếu trong thực thể phụ thuộc Tên thuộc tính khóa ngoại trong thực thể phụ thuộc Tên thuộc tính khóa chính Tên cột khóa ngoại trong DB
Grade GradeId GradeId GradeId
Grade - GradeId GradeId
Grade - Id GradeId
CurrentGrade CurrentGradeId GradeId CurrentGradeId
CurrentGrade - GradeId CurrentGradeGradeId
CurrentGrade - Id CurrentGradeId
CurrentGrade GradeId Id GradeId

Index

Theo mặc định, EF Core tạo clustered index trên các cột khóa chính và non-clustered index trên các cột khóa ngoại.

Quy ước về mối quan hệ một-nhiều trong EF Core

Ở phần này, bạn sẽ tìm hiểu về các quy ước mối quan hệ giữa hai lớp thực thể dẫn đến mối quan hệ một-nhiều giữa các bảng tương ứng trong cơ sở dữ liệu.

Entity Framework Core tuân theo quy ước tương tự như các quy ước mối quan hệ một-nhiều trong Entity Framework 6.x. Sự khác biệt duy nhất là EF Core tạo cột khóa ngoại có cùng tên với tên thuộc tính điều hướng chứ không phải là <NavigationPropertyName>_<PrimaryKeyPropertyName> như trong Entity Framework.

Chúng ta sẽ xem các quy ước khác nhau trong EF Core tự động cấu hình mối quan hệ một-nhiều giữa các thực thể StudentGrade.


public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}
       
public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}

Sau khi áp dụng các quy ước cho mối quan hệ một-nhiều trong các thực thể ở trên, các bảng cơ sở dữ liệu cho các thực thể StudentGrade sẽ trông giống như hình bên dưới, trong đó bảng Students có khóa ngoại GradeId.

Quy ước về quan hệ một nhiều trong Entity Framework Core

Quy ước về mối quan hệ một-một trong EF Core

Entity Framework Core đã giới thiệu các quy ước mặc định tự động định cấu hình mối quan hệ một-một giữa hai thực thể (EF 6.x hoặc trước đó không hỗ trợ các quy ước cho mối quan hệ một-một).

Trong EF Core, mối quan hệ một-một đòi hỏi một thuộc tính điều hướng tham chiếu ở cả hai bên. Sau đây các thực thể StudentStudentAddress tuân theo quy ước cho mối quan hệ một-một.


public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
       
    public StudentAddress Address { get; set; }
}

public class StudentAddress
{
    public int StudentAddressId { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public int StudentId { get; set; }
    public Student Student { get; set; }
}

Trong ví dụ trên, thực thể Student có một thuộc tính điều hướng tham chiếu thuộc kiểu StudentAddress và thực thể StudentAddress có một thuộc tính khóa ngoại StudentId và thuộc tính tham chiếu tương ứng của nó là Student.

Điều này sẽ dẫn đến mối quan hệ một-một trong các bảng tương ứng StudentsStudentAddresses trong cơ sở dữ liệu, như được hiển thị trong hình bên dưới.

Quy ước về mối quan hệ một-một trong EF Core

EF Core tạo một unique index trên cột khóa ngoại StudentId NotNull trong bảng StudentAddresses, như được hiển thị ở trên.

Điều này đảm bảo rằng giá trị của cột khóa ngoại StudentId phải là duy nhất trong bảng StudentAddress, điều cần thiết cho mối quan hệ một-một.

Lưu ý: Ràng buộc duy nhất được hỗ trợ trong Entity Framework Core nhưng không phải trong EF 6 và đó là lý do tại sao EF Core có các quy ước cho mối quan hệ một-một còn EF 6.x thì không có.

Ngoài ra bạn cũng có thể sử dụng Fluent API để cấu hình mối quan hệ một-một nếu các thực thể không tuân theo các quy ước.

Bạn có thể xem hướng dẫn đầy đủ về các quy ước trong Entity Framework Core ở bài viết sau:

Các quy ước trong Entity Framework Core | Comdy
Các quy ước trong Entity Framework Core để tạo lược đồ, bảng, cột, khóa chính, khóa ngoại, ... trong cơ sở dữ liệu.

Cấu hình trong Entity Framework Core

Bạn đã tìm hiểu về các quy ước mặc định trong EF Core trong hướng dẫn trước. Đôi khi chúng ta muốn tùy chỉnh ánh xạ thực thể thành bảng cơ sở dữ liệu và không muốn tuân theo các quy ước mặc định.

EF Core cho phép chúng ta cấu hình các lớp thực thể để tùy chỉnh ánh xạ mô hình EF thành cơ sở dữ liệu. Mẫu lập trình này được gọi là Quy ước về Cấu hình.

Có hai cách để cấu hình các lớp thực thể trong EF Core (giống như trong EF 6).

  1. Cấu hình bằng cách sử dụng các attribute chú thích dữ liệu.
  2. Cấu hình bằng cách sử dụng Fluent API.

Attribute chú thích dữ liệu trong EF Core

Chú thích dữ liệu là một phương thức cấu hình dựa trên các attribute trong đó các attribute .NET khác nhau có thể được áp dụng cho các lớp thực thể và các thuộc tính để cấu hình mô hình.

Lưu ý: Bài viết sử dụng thuật ngữ gốc là attribute (thuộc tính) để tránh nhầm lẫn với các thuộc tính (property) của lớp.

Các attribute chú thích dữ liệu không chỉ dành riêng cho Entity Framework, mà chúng cũng được sử dụng trong ASP.NET MVC.

Đây là lý do tại sao các attribute này được đặt trong namespace riêng biệt là System.ComponentModel.DataAnnotations.

Ví dụ sau đây cho thấy cách các attribute chú thích dữ liệu có thể được áp dụng cho một lớp thực thể và các thuộc tính để ghi đè các quy ước mặc định.


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

Các attribute chú thích dữ liệu trong EF 6 và EF Core thì giống nhau. Do đó bạn có thể truy cập bài viết Attribute chú thích dữ liệu trong Entity Framework để biết thêm thông tin.

Attribute chú thích dữ liệu trong Entity Framework | Comdy
Attribute chú thích dữ liệu trong Entity Framework là gì? Có những loại attribute chú thích dữ liệu nào?

Fluent API trong EF Core

Một cách khác để cấu hình các lớp thực thể là bằng cách sử dụng Entity Framework Fluent API. EF 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.

Trong Entity Framework Core, lớp ModelBuilder hoạt động như một Fluent API. Bằng cách sử dụng nó, chúng ta có thể cấu hình nhiều thứ khác nhau, vì nó cung cấp nhiều tùy chọn cấu hình hơn các attribute chú thích dữ liệu.

Entity Framework Core Fluent API cấu hình các khía cạnh sau của một mô hình:

  1. Cấu hình mô hình: Cấu hình mô hình EF để ánh xạ vào cơ sở dữ liệu. Cấu hình lượng đồ mặc định, các hàm DB, các thuộc tính và thực thể chú thích dữ liệu bổ sung được loại trừ khỏi ánh xạ.
  2. Cấu hình thực thể: Cấu hình thực thể cho bảng và ánh xạ mối quan hệ, ví dụ: khóa chính, khóa thay thể (AlternateKey), Index, tên bảng, các mối quan hệ như một-một, một-nhiều, nhiều-nhiều, v.v.
  3. Cấu hình thuộc tính: Cấu hình thuộc tính để ánh xạ cột, ví dụ tên cột, giá trị mặc định, nullable, khóa ngoại, kiểu dữ liệu, cột chống xung đột, v.v.

Bảng sau liệt kê các phương thức quan trọng cho từng loại cấu hình.

Phương thức Mô tả
Cấu hình mô hình
HasDbFunction Cấu hình chức năng cơ sở dữ liệu.
HasDefaultSchema Chỉ định lược đồ cơ sở dữ liệu mặc định.
HasAnnotation Thêm hoặc cập nhật các attribute chú thích dữ liệu trên thực thể.
HasSequence Cấu hình chuỗi cơ sở dữ liệu.
Cấu hình thực thể
HasAlternateKey Cấu hình khóa thay thế trong mô hình EF cho thực thể.
HasIndex Cấu hình thuộc tính Index cho kiểu thực thể.
HasKey Cấu hình thuộc tính khóa chính cho kiểu thực thể.
HasMany Cấu hình phần nhiều của mối quan hệ một-nhiều hoặc nhiều-nhiều.
HasOne Cấu hình phần một của mối quan hệ một-nhiều hoặc một-một.
Ignore Cấu hình lớp thực thể hoặc thuộc tính không được ánh xạ vào bảng hoặc cột trong cơ sở dữ liệu.
OwnsOne Cấu hình mối quan hệ trong đó thực thể đích được sở hữu bởi thực thể này. Giá trị khóa thực thể đích được truyền từ thực thể mà nó thuộc về.
ToTable Cấu hình tên bảng cho thực thể.
Cấu hình thuộc tính
HasColumnName Cấu hình tên cột tương ứng của một thuộc tính trong cơ sở dữ liệu.
HasColumnType Cấu hình kiểu dữ liệu của cột tương ứng của một thuộc tính trong cơ sở dữ liệu.
HasComputedColumnSql Cấu hình thuộc tính để ánh xạ tới cột được tính toán trong cơ sở dữ liệu.
HasDefaultValue Cấu hình giá trị mặc định cho cột mà thuộc tính ánh xạ.
HasDefaultValueSql Cấu hình biểu thức giá trị mặc định cho cột mà thuộc tính ánh xạ.
HasField Chỉ định trường sao lưu được sử dụng với thuộc tính.
HasMaxLength Cấu hình độ dài tối đa của dữ liệu có thể được lưu trữ trong một thuộc tính.
IsConcurrencyToken Cấu hình thuộc tính được sử dụng làm mã thông báo xung đột.
IsRequired Cấu hình thuộc tính bắt buộc nhập dữ liệu khi gọi phương thức SaveChanges.
IsRowVersion Cấu hình thuộc tính được sử dụng để phát hiện xung đột.
IsUnicode Cấu hình thuộc tính chuỗi có thể chứa các ký tự unicode hoặc không.
ValueGeneratedNever Cấu hình một thuộc tính không thể có giá trị được tạo tự động khi thực thể được lưu.
ValueGeneratedOnAdd Cấu hình rằng thuộc tính có giá trị được tạo tự động khi lưu thực thể mới.
ValueGeneratedOnAddOrUpdate Cấu hình rằng thuộc tính có giá trị được tạo tự động khi lưu thực thể mới hoặc cập nhật thực thể hiện có.
ValueGeneratedOnUpdate Cấu hình rằng thuộc tính có giá trị được tạo tự động khi cập nhật thực thể hiện có.

Cấu hình Fluent API trong EF Core

Ghi đè phương thức OnModelCreating và sử dụng tham số modelBuilder kiểu ModelBuilder để cấu hình các lớp thực thể như ví dụ bên dưới.


public class SchoolDBContext: DbContext 
{
    public DbSet<Student> Students { get; set; }
        
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //Write Fluent API configurations here

        //Property Configurations
        modelBuilder.Entity<Student>()
                .Property(s => s.StudentId)
                .HasColumnName("Id")
                .HasDefaultValue(0)
                .IsRequired();
    }
}

Trong ví dụ trên, thể hiện của ModelBuilder được sử dụng để cấu hình một thuộc tính bằng cách gọi một chuỗi nhiều phương thức.

Nó cấu hình thuộc tính StudentId của thực thể Student:

  • StudentId có tên là Id trong cơ sở dữ liệu bằng cách sử dụng phương thức HasColumnName
  • StudentId có giá trị mặc định là 0 bằng cách sử dụng phương thức HasDefaultValue
  • StudentId cho phép nullable bằng phương thức IsRequired

Các cấu hình trên được thực hiện trong một câu lệnh thay vì nhiều câu lệnh. Điều này làm tăng khả năng đọc và cũng mất ít thời gian hơn để viết so với nhiều câu lệnh, như được trình bày bên dưới.


//Fluent API method chained calls
modelBuilder.Entity<Student>()
        .Property(s => s.StudentId)
        .HasColumnName("Id")
        .HasDefaultValue(0)
        .IsRequired();

//Separate method calls
modelBuilder.Entity<Student>().Property(s => s.StudentId).HasColumnName("Id");
modelBuilder.Entity<Student>().Property(s => s.StudentId).HasDefaultValue(0);
modelBuilder.Entity<Student>().Property(s => s.StudentId).IsRequired();
Lưu ý: Cấu hình Fluent API có quyền ưu tiên cao hơn các attribute chú thích dữ liệu.

Bạn có thể xem hướng dẫn đầy đủ về cấu hình trong Entity Framework Core tại đây:

Cấu hình trong Entity Framework Core | Comdy
Hướng dẫn Cấu hình trong Entity Framework Core sử dụng attribute chú thích dữ liệu và Fluent API.

Truy vấn trong Entity Framework Core

Truy vấn trong Entity Framework Core vẫn giống như truy vấn trong Entity Framework 6.x, với các truy vấn SQL được tối ưu hóa hơn và khả năng đưa các phương thức C# / VB.NET vào các truy vấn LINQ-to-Entities.

Ở phần này, bạn sẽ tìm hiểu các tính năng mới của truy vấn được giới thiệu trong Entity Framework Core.

Phương thức C# / VB.NET trong truy vấn

EF Core có một tính năng mới trong LINQ-to-Entities nơi chúng ta có thể thêm các phương thức C# hoặc VB.NET trong truy vấn. Điều này là không thể trong EF 6.


private static void Main(string[] args)
{
    var context = new SchoolContext();
    var studentsWithSameName = context.Students
                                      .Where(s => s.FirstName == GetName())
                                      .ToList();
}

public static string GetName() 
{
    return "Bill";
}

Eager Loading trong EF Core

Eager Loading là gì?

Eager Loading là quá trình trong đó một truy vấn cho một kiểu thực thể cũng tải các thực thể liên quan như một phần của truy vấn, do đó chúng ta không cần phải thực hiện một truy vấn riêng cho các thực thể liên quan.

Entity Framework Core hỗ trợ Eager Loading các thực thể liên quan, giống như EF 6, sử dụng phương thức mở rộng Include() và truy vấn tạo kết quả đầu ra.

Ngoài ra, nó cũng cung cấp phương thức mở rộng ThenInclude() để tải nhiều cấp độ của các thực thể liên quan. (EF 6 không hỗ trợ phương thức ThenInclude())

Phương thức Include trong EF Core

Không giống như EF 6, chúng ta có thể chỉ định biểu thức lambda làm tham số trong phương thức Include() để chỉ định thuộc tính điều hướng như dưới đây.


var context = new SchoolContext();

var studentWithGrade = context.Students
                           .Where(s => s.FirstName == "Bill")
                           .Include(s => s.Grade)
                           .FirstOrDefault();

Phương thức ThenInclude trong EF Core

EF Core đã giới thiệu phương thức mở rộng ThenInclude() mới để tải nhiều cấp độ của các thực thể liên quan. Hãy xem xét ví dụ sau:


var context = new SchoolContext();

var student = context.Students.Where(s => s.FirstName == "Bill")
                        .Include(s => s.Grade)
                            .ThenInclude(g => g.Teachers)
                        .FirstOrDefault();

Truy vấn tạo kết quả đầu ra

Chúng tôi cũng có thể tải nhiều thực thể liên quan bằng cách sử dụng truy vấn tạo kết quả đầu ra thay vì sử dụng phương thức Include() hoặc ThenInclude().

Ví dụ sau đây cho thấy các truy vấn tạo kết quả đầu ra để tải các thực thể Student, GradeTeacher.


var context = new SchoolContext();

var stud = context.Students.Where(s => s.FirstName == "Bill")
                        .Select(s => new
                        {
                            Student = s,
                            Grade = s.Grade,
                            GradeTeachers = s.Grade.Teachers
                        })
                        .FirstOrDefault();

Lazy loading trong EF Core

Lazy loading trì hoãn việc tải các dữ liệu liên quan, cho đến khi bạn yêu cầu cụ thể. Lazy loading đối lập hoàn toàn với Eager loading.

Lazy loading được giới thiệu trong EF Core 2.1 như một tính năng tùy chọn bổ sung.

Lazy loading có thể được kích hoạt theo hai cách:

  • Sử dụng Proxies
  • Sử dụng interface ILazyLoader

Explicit Loading trong EF Core

Ở phần này bạn sẽ tìm hiểu làm thế nào để tải các thực thể liên quan trong một biểu đồ thực thể rõ ràng.

Sử dụng phương thức Load() để tải các thực thể liên quan một cách rõ ràng. Hãy xem xét ví dụ sau.

using (var context = new SchoolContext())
{
    var student = context.Students
                         .Where(s => s.FirstName == "Bill")
                         .FirstOrDefault<Student>();
    
    // loads StudentAddress
    context.Entry(student).Reference(s => s.StudentAddress).Load(); 
    
    // loads Courses collection 
    context.Entry(student).Collection(s => s.StudentCourses).Load();
}

Bạn có thể xem hướng dẫn đầy đủ về truy vấn trong Entity Framework Core ở bài viết này:

Truy vấn trong Entity Framework Core | Comdy
Truy vấn trong Entity Framework Core có gì mới? Truy vấn trong EF Core khác EF ở những điểm nào.

Kịch bản lưu dữ liệu trong Entity Framework Core

Entity Framework Core cung cấp các cách khác nhau để thêm, cập nhật hoặc xóa dữ liệu trong cơ sở dữ liệu. Một thực thể chứa dữ liệu trong thuộc tính của nó sẽ được thêm hoặc cập nhật hoặc xóa dựa trên trạng thái EntityState của nó.

Có hai kịch bản để lưu dữ liệu thực thể: được kết nối và ngắt kết nối. Trong kịch bản được kết nối, cùng một thể hiện DbContext được sử dụng trong việc truy xuất và lưu các thực thể, trong khi điều này khác với kịch bản ngắt kết nối.

Lưu dữ liệu trong kịch bản được kết nối trong EF Core

Hình dưới đây minh họa các thao tác CUD (Create, Update, Delete) trong kịch bản được kết nối.

Lưu dữ liệu trong kịch bản được kết nối

Theo hình trên, Entity Framework xây dựng và thực thi các câu lệnh INSERT, UPDATE hoặc DELETE cho các thực thể có trạng thái EntityState là Added, Modified hoặc Deleted khi phương thức DbContext.SaveChanges() được gọi.

Trong kịch bản được kết nối, một thể hiện DbContext theo dõi tất cả các thực thể và do đó, nó sẽ tự động thiết lập một trạng thái EntityState phù hợp cho mỗi thực thể bất cứ khi nào một thực thể được tạo, sửa đổi hoặc xóa.

Lưu dữ liệu trong kịch bản ngắt kết nối trong EF Core

Lưu dữ liệu trong kịch bản ngắt kết nối hơi khác một chút so với kịch bản được kết nối.

Trong kịch bản bị ngắt kết nối, DbContext không biết các thực thể bị ngắt kết nối vì các thực thể đã được thêm hoặc sửa đổi ngoài phạm vi của thể hiện DbContext hiện tại.

Vì vậy, bạn cần đính kèm (attach) các thực thể bị ngắt kết nối vào một Context với trạng thái EntityState phù hợp để thực hiện các thao tác CUD (Create, Update, Delete) vào cơ sở dữ liệu.

Hình dưới đây minh họa các thao tác CUD trong kịch bản ngắt kết nối:

Lưu dữ liệu trong kịch bản ngắt kết nối trong EF Core

Theo hình trên, các thực thể bị ngắt kết nối (các thực thể không được theo dõi bởi DbContext) cần phải được đính kèm vào DbContext với trạng thái EntityState phù hợp.

Ví dụ: Trạng thái Added cho các thực thể mới, trạng thái Modified cho các thực thể đã chỉnh sửa và trạng thái Deleted cho các thực thể bị xóa, điều này sẽ thực thi các lệnh INSERT, UPDATE hoặc DELETE trong cơ sở dữ liệu khi phương thức SaveChanges() được gọi.

Bạn có thể xem hướng dẫn đầy đủ về kịch bản lưu dữ liệu trong Entity Framework Core ở bài viết này:

2 kịch bản lưu dữ liệu trong Entity Framework Core | Comdy
2 kịch bản lưu dữ liệu trong Entity Framework Core là kịch bản được kết nối và kịch bản ngắt kết nối.

Còn tiếp



Bài viết liên quan:

2 kịch bản lưu dữ liệu trong Entity Framework Core là kịch bản được kết nối và kịch bản ngắt kết nối.

Tạo ứng dụng .NET Core Console đầu tiên và cấu hình sử dụng Entity Framework Core.

Truy vấn trong Entity Framework Core có gì mới? Truy vấn trong EF Core khác EF ở những điểm nào.