Bộ chuyển đổi giá trị của Entity Framework Core 5

Tôi đã tìm hiểu nhiều về Entity Framework Core 5 và tất cả những gì có thể về hoạt động bên trong của trình ánh xạ quan hệ đối tượng (ORM). Với EF Core 5, ORM mới hơn đã có những bước cải tiến mạnh mẽ so với Entity Framework.

Bài viết này sẽ xem xét khả năng mở rộng mà nhiều người sẽ đánh giá cao, đặc biệt là khi xử lý các đối tượng C# mà chúng ta có thể quản lý bằng số định danh. Hãy đi sâu tìm hiểu về bộ chuyển đổi giá trị của EF Core 5.

Bộ chuyển đổi giá trị là gì?

Khi xử lý mô hình dữ liệu EF Core, chúng ta có thể lưu trữ dữ liệu khác theo cách chúng ta xử lý nó trong ngữ cảnh ứng dụng của chúng ta.

Bộ chuyển đổi giá trị cho phép các giá trị thuộc tính được chuyển đổi khi đọc hoặc ghi vào cơ sở dữ liệu. Sự chuyển đổi này có thể là từ giá trị này sang giá trị khác cùng kiểu (ví dụ: mã hóa chuỗi) hoặc từ giá trị của một kiểu này sang giá trị của kiểu khác (ví dụ: chuyển đổi giá trị enum thành chuỗi và ngược lại trong cơ sở dữ liệu.) - TÀI LIỆU MICROSOFT

Chúng ta có thể sử dụng bộ chuyển đổi giá trị để tối ưu hóa lưu trữ cơ sở dữ liệu, hiệu suất ghi và độ trễ đọc, ngoài việc tối ưu hóa bộ nhớ trong ứng dụng của chúng ta.

Hãy xem cách chúng ta sử dụng bộ chuyển đổi giá trị để tối ưu hóa thông tin trùng lặp trong cơ sở dữ liệu của chúng ta.

Làm việc với bộ chuyển đổi giá trị

Hãy bắt đầu với một mô hình cơ sở dữ liệu, thực thể Movie này sẽ chứa một thuộc tính kiểu record là StreamingService. Mặc dù có vẻ như một dịch vụ phát trực tuyến mới sẽ khởi chạy vài phút một lần, nhưng chúng ta có thể giả sử rằng tập dữ liệu này là hữu hạn.

public class Movie
{
    public int Id { get; set; }
    public string Name { get; set; } 
    public StreamingService StreamingService { get; set; }
}

public record StreamingService(string Id, string Description)
{
    public static StreamingService Netflix { get; } 
        = new("netflix", "Netflix streaming service");
    public static StreamingService Hulu { get; }
        = new("hulu", "Hulu");
    public static StreamingService HBOMax { get; }
        = new("hbo-max", "HBO Max");
    public static StreamingService DisneyPlus { get; }
        = new("disney-plus", "Disney+");
    public static StreamingService AppleTvPlus { get; }
        = new("apple-tv-plus", "Apple TV+");

    public static IReadOnlyList<StreamingService> All = new[] {
        Netflix, 
        Hulu,
        HBOMax,
        DisneyPlus,
        AppleTvPlus
    };
}

Sẽ là lãng phí thông lượng mạng và bộ nhớ để truyền nhiều dữ liệu hơn so với Id tới cơ sở dữ liệu của chúng ta. Chúng ta có thể lưu trữ phần còn lại của thông tin trong các thể hiện của record C# của chúng ta. Chưa kể rằng việc sao chép các thực thể chỉ đọc này sẽ thêm vào việc sử dụng bộ nhớ của ứng dụng của chúng ta.

Hãy xem cách yêu cầu EF Core dịch các record của chúng ta thành các giá trị string mà chúng ta có thể lưu trữ trong cơ sở dữ liệu của mình.

public class Database
    : DbContext
{
    public DbSet<Movie> Movies { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .EnableSensitiveDataLogging()
            .LogTo(Console.WriteLine)
            .UseSqlite("Data Source=movies.db");

        base.OnConfiguring(optionsBuilder);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        var converter = new ValueConverter<StreamingService, string>(
            from => from.Id,
            to => StreamingService.All.FirstOrDefault(s => s.Id == to)
        );

        modelBuilder
            .Entity<Movie>()
            .Property(m => m.StreamingService)
            .HasConversion(converter);
        
        base.OnModelCreating(modelBuilder);
    }
}

Trong phương thức OnModelCreating, chúng ta có thể định nghĩa một thể hiện mới của lớp ValueConverter với hai tham số kiểu Func. EF Core 5 sẽ sử dụng hai tham số này để chuyển đổi tới và từ record StreamingService của chúng ta sang cột mục tiêu có kiểu string.

Sử dụng ModelBuilder, chúng ta có thể nói với EF Core rằng thuộc tính StreamingService của chúng ta có thể chuyển đổi bằng cách sử dụng thể hiện của ValueConverter.

Hãy tạo quá trình di chuyển của chúng ta để thấy rằng EF Core đã quản lý để hiểu được ánh xạ của chúng ta một cách chính xác.

public partial class initial : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movies",
            columns: table => new
            {
                Id = table.Column<int>(type: "INTEGER", nullable: false)
                    .Annotation("Sqlite:Autoincrement", true),
                Name = table.Column<string>(type: "TEXT", nullable: true),
                StreamingService = table.Column<string>(type: "TEXT", nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movies", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movies");
    }
}

Như chúng ta có thể thấy, cột StreamingService có kiểu TEXT vì chúng ta đang sử dụng SQLite trong trường hợp này.

Viết truy vấn với bộ chuyển đổi giá trị

Bộ chuyển đổi giá trị của chúng ta mong muốn sử dụng các thể hiện của StreamingService trong các truy vấn LINQ của chúng ta.

// 👎 wrong
var results =
    await catalog
        .Movies
        .Where(m => m.StreamingService.Id == StreamingService.Netflix.Id)
        .ToListAsync(); 

// 👍 correct
var results =
    await catalog
        .Movies
        .Where(m => m.StreamingService == StreamingService.Netflix)
        .ToListAsync(); 

Cần nhớ rằng EF Core chỉ biết về thực thể cấp cao nhất của chúng ta. Nó không biết rằng StreamingService có các thuộc tính Id Description. Cố gắng sử dụng bất kỳ thuộc tính nào của StreamingService trong một truy vấn sẽ kết thúc trong một ngoại lệ.

Hãy xem ví dụ mẫu hoàn chỉnh.

using System;
using System.Linq;
using EFCoreValueConverters;
using Microsoft.EntityFrameworkCore;

// save new movie
await using var catalog = new Database();
catalog.Movies.Add(new Movie {
    Name = "Birdbox", 
    StreamingService = StreamingService.Netflix
});
await catalog.SaveChangesAsync();

// retrieve
await using var browser = new Database();

var results =
    await catalog
        .Movies
        .Where(m => m.StreamingService == StreamingService.Netflix)
        .ToListAsync(); 

foreach (var result in results) {
    Console.WriteLine($"{result.Name} now streaming on {result.StreamingService.Description}!");        
}

Phân tích cú pháp thông qua nhật ký của EF Core, chúng ta có thể thấy một vài điều. Đầu tiên, hãy xem cách chúng ta đã lưu bộ phim của mình.

Executing DbCommand [Parameters=[@p0='Birdbox' (Size = 7), @p1='netflix' (Size = 7)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Movies" ("Name", "StreamingService")
VALUES (@p0, @p1);
SELECT "Id"
FROM "Movies"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Chúng ta có thể thấy rằng netflix đã được sử dụng như một tham số, nghĩa là EF Core đã ánh xạ record StreamingService của chúng ta một cách chính xác. Tiếp theo, hãy xem truy vấn đọc của chúng ta.

Executed DbCommand (1ms) [Parameters=[@__Netflix_0='netflix' (Size = 7)], CommandType='Text', CommandTimeout='30']
SELECT "m"."Id", "m"."Name", "m"."StreamingService"
FROM "Movies" AS "m"
WHERE "m"."StreamingService" = @__Netflix_0

Hãy nhớ rằng, mệnh đề where LINQ của chúng ta đã sử dụng một thể hiện của StreamingService.

var results =
    await catalog
        .Movies
        .Where(m => m.StreamingService == StreamingService.Netflix)
        .ToListAsync(); 

Cuối cùng, chúng ta thấy kết quả sau.

Birdbox now streaming on Netflix streaming service!

Thật tuyệt vời! Thật không thể tin được!

Phần kết luận

Bộ chuyển đổi giá trị cho phép chúng ta ánh xạ các kiểu tương đối phức tạp có thể không được hỗ trợ bởi các cơ sở dữ liệu.

Trong ví dụ trên, chúng ta đã có thể lưu trữ thông tin người dùng quan trọng trong lớp ứng dụng của mình trong khi vẫn lưu trữ giá trị tối thiểu cần thiết để xây dựng lại các mô hình của chúng ta.

Việc sử dụng lại các record chỉ đọc giúp chúng ta tiết kiệm bộ nhớ trong các tình huống quan trọng. Như chúng ta đã thấy trong mẫu, việc xác định bộ chuyển đổi giá trị rất đơn giản.

Lợi ích bổ sung của bộ chuyển đổi giá trị là nó cho phép các nhà cung cấp bên thứ ba tích hợp trực tiếp vào EF Core, cho phép hệ sinh thái phát triển mạnh hơn so với kiến ​​trúc Entity Framework 6 trước đó.

Nếu bạn đang sử dụng bộ chuyển đổi giá trị, vui lòng để lại nhận xét bên dưới và cho tôi biết cách thực hiện.

Và nếu bạn thấy bài viết này hữu ích, hãy chia sẻ nó với đồng nghiệp và bạn bè. Như mọi khi, cảm ơn bạn đã đọc.

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

Bạn có thể vui lòng đưa Comdy vào whitelist của trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi trong việc trả tiền cho dịch vụ lưu trữ web để duy trì hoạt động của trang web.

Entity Framework Core
Bài Viết Liên Quan:
Truy vấn SQL thô với Entity Framework Core 5
Trung Nguyen 11/11/2021
Truy vấn SQL thô với Entity Framework Core 5

Trong bài viết này, chúng ta sẽ khám phá cách làm việc với các tính năng của Entity Framework Core 5 (EF Core) để thực thi truy vấn SQL thô.

Cách thêm view vào DbContext trong Entity Framework Core
Trung Nguyen 07/11/2021
Cách thêm view vào DbContext trong Entity Framework Core

Với EF Core 5, chúng ta có thể thêm view vào trong DbContext và tạo view trong database bằng cách sử dụng cơ chế chuyển đổi cơ sở dữ liệu tích hợp sẵn.

Mô hình hóa các mối quan hệ SQL trong EF Core
Trung Nguyen 03/11/2021
Mô hình hóa các mối quan hệ SQL trong EF Core

Bài viết này sẽ khám phá các mối quan hệ trong cơ sở dữ liệu quan hệ và cách mô hình hóa các mối quan hệ đó bằng cách tiếp cận Code First trong EF Core.

Dữ liệu đệ quy với Entity Framework Core và SQL Server
Trung Nguyen 01/11/2021
Dữ liệu đệ quy với Entity Framework Core và SQL Server

Trong bài viết này, chúng ta sẽ khám phá cách lập mô hình dữ liệu đệ quy với Entity Framework Core và SQL Server