0
0
Lập trình
Flame Kris
Flame Krisbacodekiller

Khám Phá Quy Trình Lấy Dữ Liệu EF Core từ SQL Server

Đăng vào 8 tháng trước

• 9 phút đọc

Chủ đề:

KungFuTech

Khám Phá Quy Trình Lấy Dữ Liệu từ EF Core tới SQL Server

Entity Framework Core (EF Core) là framework ánh xạ đối tượng - quan hệ (ORM) hiện đại của Microsoft, giúp kết nối các ứng dụng .NET với cơ sở dữ liệu. Mặc dù các nhà phát triển thường sử dụng EF Core thông qua các API LINQ cấp cao, việc hiểu rõ quy trình phức tạp mà EF Core sử dụng để lấy dữ liệu từ SQL Server sẽ giúp tối ưu hiệu suất và xử lý sự cố hiệu quả hơn.

Mục Lục

  • Tổng Quan Kiến Trúc
  • Quy Trình Truy Vấn
  • Quá Trình Chuyển Đổi LINQ sang SQL
  • Quản Lý Kết Nối và Tái Sử Dụng
  • Tinh Chất Kết Quả
  • Tích Hợp Theo Dõi Thay Đổi
  • Các Xem Xét Về Hiệu Suất
  • Tình Huống Nâng Cao

Tổng Quan Kiến Trúc

Quy trình lấy dữ liệu của EF Core bao gồm nhiều tầng làm việc cùng nhau:

Copy
Application Layer (Truy Vấn LINQ)
         ↓
Query Pipeline (Dịch & Tối Ưu)
         ↓
Database Provider (Nhà Cung Cấp SQL Server)
         ↓
ADO.NET Core (SqlConnection, SqlCommand)
         ↓
Giao Thức TDS (Tabular Data Stream)
         ↓
SQL Server Database Engine

Các Thành Phần Chính

DbContext: Lớp chính chịu trách nhiệm cho các thao tác cơ sở dữ liệu, duy trì trạng thái thực thể và điều phối quy trình lấy dữ liệu.

IQueryable Provider: Thực hiện giao diện IQueryProvider để xử lý cây biểu thức LINQ và chuyển đổi chúng thành các truy vấn cơ sở dữ liệu có thể thực thi.

Database Provider: Triển khai cụ thể cho SQL Server, chuyển đổi các thao tác cơ sở dữ liệu tổng quát thành các lệnh tương thích với SQL Server.

Change Tracker: Giám sát trạng thái thực thể và quản lý vòng đời đối tượng trong quá trình lấy dữ liệu.

Quy Trình Truy Vấn

Khi bạn thực hiện một truy vấn LINQ trên một DbSet<T>, EF Core khởi động một quy trình tinh vi:

1. Phân Tích Cây Biểu Thức

Copy
// Truy vấn LINQ này
var users = context.Users
    .Where(u => u.Age > 18)
    .OrderBy(u => u.LastName)
    .Take(10);

// Tạo một cây biểu thức mà EF Core sẽ phân tích

EF Core nhận cây biểu thức LINQ như một cây Expression, đại diện cho cấu trúc truy vấn theo định dạng phân cấp. QueryCompiler phân tích cây này để hiểu các thao tác dự kiến.

2. Tạo Mô Hình Truy Vấn

Cây biểu thức được chuyển đổi thành một QueryModel nội bộ đại diện cho:

  • Các loại thực thể liên quan
  • Các điều kiện lọc
  • Các yêu cầu sắp xếp
  • Các thông số chiếu
  • Các mối quan hệ kết nối

3. Tối Ưu Truy Vấn

EF Core áp dụng nhiều tối ưu hóa:

  • Predicate Pushdown: Di chuyển các mệnh đề WHERE gần đến nguồn dữ liệu nhất có thể
  • Join Elimination: Loại bỏ các phép nối không cần thiết khi có thể
  • Subquery Flattening: Chuyển đổi các truy vấn lồng vào những dạng hiệu quả hơn

Quá Trình Chuyển Đổi LINQ sang SQL

Sự chuyển đổi từ LINQ sang SQL bao gồm nhiều giai đoạn:

Các Visitor Biểu Thức

EF Core sử dụng mẫu Visitor để duyệt qua các cây biểu thức. Các visitor chính bao gồm:

Copy
// Đại diện đơn giản về cách EF Core xử lý các biểu thức
public class SqlTranslatingExpressionVisitor : ExpressionVisitor
{
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        // Chuyển đổi các phương thức LINQ như Where, Select, OrderBy thành các tương đương SQL
        if (node.Method.Name == "Where")
        {
            return TranslateWhereClause(node);
        }
        // ... các chuyển đổi phương thức khác
    }
}

Tạo SQL

Các triển khai của giao diện IQuerySqlGenerator tạo ra SQL cuối cùng:

Copy
-- Tạo từ truy vấn LINQ ở trên
SELECT TOP(10) [u].[Id], [u].[FirstName], [u].[LastName], [u].[Age]
FROM [Users] AS [u]
WHERE [u].[Age] > 18
ORDER BY [u].[LastName]

Ràng Buộc Tham Số

EF Core tự động tham số hóa các truy vấn để ngăn chặn SQL injection và cho phép tái sử dụng kế hoạch truy vấn:

Copy
var minAge = 18;
var users = context.Users.Where(u => u.Age > minAge);
// Trở thành: WHERE [u].[Age] > @p0

Quản Lý Kết Nối và Tái Sử Dụng

Vòng Đời DbConnection

EF Core quản lý kết nối cơ sở dữ liệu thông qua một hệ thống tinh vi:

  1. Mua Kết Nối: Lấy từ hồ bơi kết nối hoặc tạo mới
  2. Thực Thi Lệnh: Thực thi lệnh SQL với việc xử lý thời gian chờ hợp lý
  3. Giải Phóng Kết Nối: Trả về hồ bơi hoặc hủy bỏ dựa trên vòng đời ngữ cảnh

Tái Sử Dụng Kết Nối

Copy
// Cấu hình tái sử dụng kết nối
services.AddDbContextPool<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString), 
    poolSize: 128);

Hồ bơi kết nối duy trì một tập hợp các kết nối có thể tái sử dụng, giảm thiểu chi phí thiết lập kết nối.

Điều Phối Giao Dịch

EF Core phối hợp với hệ thống giao dịch của SQL Server:

Copy
using var transaction = context.Database.BeginTransaction();
try
{
    var data = context.Users.Where(u => u.Active).ToList();
    // Dữ liệu được lấy trong phạm vi giao dịch
    transaction.Commit();
}
catch
{
    transaction.Rollback();
    throw;
}

Tinh Chất Kết Quả

Xử Lý Đọc Dữ Liệu

Khi SQL Server trả về kết quả, EF Core xử lý chúng thông qua:

  1. SqlDataReader: Đọc dữ liệu theo chiều tiến của ADO.NET
  2. Chuyển Đổi Giá Trị: Chuyển đổi các loại cơ sở dữ liệu thành các loại .NET
  3. Xây Dựng Thực Thể: Tạo các thể hiện thực thể
  4. Điền Thuộc Tính: Thiết lập các thuộc tính thực thể từ kết quả truy vấn

Quy Trình Tạo Thực Thể

Copy
// Quy trình tạo thực thể đơn giản
public class EntityMaterializer<T>
{
    public T MaterializeEntity(DbDataReader reader)
    {
        var entity = new T();

        // Ánh xạ từng cột tới các thuộc tính thực thể
        for (int i = 0; i < reader.FieldCount; i++)
        {
            var propertyName = reader.GetName(i);
            var value = reader.GetValue(i);

            SetPropertyValue(entity, propertyName, value);
        }

        return entity;
    }
}

Tải Thuộc Tính Điều Hướng

EF Core hỗ trợ nhiều chiến lược tải:

Tải Eager:

Copy
var users = context.Users
    .Include(u => u.Orders)
    .ThenInclude(o => o.OrderItems)
    .ToList();

Tải Lười:

Copy
// Cần proxy và thuộc tính điều hướng ảo
public virtual ICollection<Order> Orders { get; set; }

Tải Rõ Ràng:

Copy
context.Entry(user)
    .Collection(u => u.Orders)
    .Load();

Tích Hợp Theo Dõi Thay Đổi

Quản Lý Trạng Thái Thực Thể

Trong quá trình lấy dữ liệu, Change Tracker của EF Core:

  1. Đăng Ký Thực Thể: Thêm các thực thể đã lấy vào hệ thống theo dõi
  2. Tạo Ảnh Chụp: Lưu trữ các giá trị gốc để phát hiện thay đổi
  3. Thiết Lập Mối Quan Hệ: Kết nối các thuộc tính điều hướng

Giải Quyết Danh Tính

EF Core đảm bảo tính danh tính của thực thể thông qua:

Copy
// Khóa chính giống nhau trả về cùng một thể hiện
var user1 = context.Users.Find(1);
var user2 = context.Users.First(u => u.Id == 1);
// user1 và user2 tham chiếu đến cùng một thể hiện đối tượng

Truy Vấn Không Theo Dõi

Đối với các tình huống chỉ đọc, vô hiệu hóa theo dõi thay đổi để cải thiện hiệu suất:

Copy
var users = context.Users
    .AsNoTracking()
    .Where(u => u.Active)
    .ToList();

Các Xem Xét Về Hiệu Suất

Bộ Nhớ Cache Biên Dịch Truy Vấn

EF Core lưu trữ các truy vấn đã biên dịch để tránh chi phí dịch lại:

Copy
// Cấu trúc truy vấn này được lưu trữ
var usersByAge = (int age) => context.Users.Where(u => u.Age > age);

// Nhiều lần gọi tái sử dụng truy vấn đã biên dịch
var adults = usersByAge(18).ToList();
var seniors = usersByAge(65).ToList();

Truy Vấn Tách Biệt cho Bộ Sưu Tập

Khi tải nhiều bộ sưu tập, xem xét truy vấn tách biệt:

Copy
var blogs = context.Blogs
    .AsSplitQuery()
    .Include(b => b.Posts)
    .Include(b => b.Tags)
    .ToList();

Chiếu Để Tối Ưu Lấy Dữ Liệu

Sử dụng chiếu để chỉ lấy dữ liệu cần thiết:

Copy
var userSummaries = context.Users
    .Select(u => new UserSummaryDto
    {
        Id = u.Id,
        FullName = u.FirstName + " " + u.LastName,
        OrderCount = u.Orders.Count()
    })
    .ToList();

Tình Huống Nâng Cao

Tích Hợp SQL Thô

EF Core cho phép kết hợp LINQ với SQL thô:

Copy
var users = context.Users
    .FromSqlRaw("SELECT * FROM Users WHERE LastLoginDate > DATEADD(day, -30, GETDATE())")
    .Where(u => u.Active)
    .OrderBy(u => u.LastName)
    .ToList();

Bộ Lọc Truy Vấn Toàn Cục

Thực hiện phân tách người thuê hoặc các mẫu xóa mềm:

Copy
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .HasQueryFilter(u => !u.IsDeleted);
}

Truy Vấn Đã Biên Dịch cho Tình Huống Hiệu Suất Cao

Tiền biên dịch các truy vấn thường xuyên sử dụng:

Copy
private static readonly Func<ApplicationDbContext, int, IEnumerable<User>> GetUsersByAge =
    EF.CompileQuery((ApplicationDbContext context, int age) =>
        context.Users.Where(u => u.Age > age));

// Sử dụng
var users = GetUsersByAge(context, 18).ToList();

Trình Chuyển Đổi Giá Trị Tùy Chỉnh

Xử lý ánh xạ kiểu phức tạp:

Copy
modelBuilder.Entity<User>()
    .Property(u => u.Settings)
    .HasConversion(
        v => JsonSerializer.Serialize(v),
        v => JsonSerializer.Deserialize<UserSettings>(v));

Giám Sát và Chẩn Đoán

Ghi Nhận Truy Vấn SQL

Kích hoạt ghi nhận chi tiết để hiểu các SQL được tạo ra:

Copy
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(connectionString)
        .LogTo(Console.WriteLine, LogLevel.Information)
        .EnableSensitiveDataLogging();
}

Các Chỉ Số Hiệu Suất

Giám sát các chỉ số chính:

  • Thời gian thực thi truy vấn
  • Sử dụng hồ bơi kết nối
  • Hiệu suất theo dõi thay đổi
  • Mô hình phân bổ bộ nhớ

Kết Luận

Hiểu quy trình lấy dữ liệu của EF Core giúp các nhà phát triển viết ứng dụng hiệu quả hơn. Quy trình truy vấn tinh vi của framework, từ phân tích biểu thức LINQ đến tạo SQL và tinh chất kết quả, mang lại cả sự tiện lợi và hiệu suất khi được hiểu và sử dụng đúng cách.

Những điểm quan trọng cần lưu ý để sử dụng EF Core tối ưu:

  • Hiểu quy trình dịch truy vấn để thiết kế truy vấn tốt hơn
  • Tận dụng các chiến lược tải phù hợp dựa trên tình huống của bạn
  • Sử dụng các chiếu và truy vấn không theo dõi cho các thao tác chỉ đọc
  • Giám sát SQL được tạo ra và các chỉ số hiệu suất
  • Cân nhắc bộ nhớ cache biên dịch và các truy vấn tách biệt cho các tình huống phức tạp

Bằng cách làm chủ những khái niệm này, các nhà phát triển có thể khai thác tối đa tiềm năng của EF Core trong khi duy trì các mẫu truy cập dữ liệu hiệu suất cao trong các ứng dụng .NET của họ.


Bài viết này cung cấp cái nhìn sâu sắc về quy trình nội bộ của EF Core. Để có các ví dụ thực hành và các thực tiễn tốt nhất, hãy tham khảo tài liệu chính thức của Microsoft và xem xét các yêu cầu cụ thể của ứng dụng của bạn khi áp dụng các khái niệm này.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào