VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
ASP.NET Core Web APIDữ liệu với EF Core
Bài 13/19
6 phút

EF Core và DbContext

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Dữ liệu trong bộ nhớ mất mỗi lần tắt server. Cửa hàng thật cần lưu sản phẩm vào database, và bạn đã có sẵn Oracle từ khoá SQL. EF Core cho phép làm việc với Oracle bằng class và LINQ quen thuộc, không phải tự viết SQL cho từng thao tác.

Khái niệm

🗄️ ORM (Object-Relational Mapper): thư viện chuyển qua lại giữa object C# và dòng trong bảng database, như EF Core của .NET.

🎒 DbContext: class đại diện cho một phiên làm việc với database, trong đó mỗi DbSet<T> ứng với một bảng.

C# Oracle
class Product, DbSet tên Products bảng PRODUCTS
property Name cột NAME
một object Product một dòng trong bảng
property Id khoá chính, tự tăng

Ví dụ

API dùng một user riêng tên shopapi, tách khỏi user shop của khoá SQL. Mở VS Code, kết nối Oracle bằng user system, mật khẩu oracle_pw, service name FREEPDB1, rồi chạy một lần:

CREATE USER shopapi IDENTIFIED BY shopapi_pw
  QUOTA UNLIMITED ON USERS;
GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE
  TO shopapi;

GRANT cấp cho user mới quyền đăng nhập và tạo bảng. User mới chưa có bảng nào, bài sau dùng migration để EF Core tự tạo bảng.

Cài package cho Oracle:

dotnet add package Oracle.EntityFrameworkCore
dotnet add package EFCore.NamingConventions

Bản mới nhất của các package EF Core cần .NET 10. Project dùng .NET 9 thì thêm --version 9.* vào lệnh dotnet add package và dotnet tool install.

Thêm chuỗi kết nối vào appsettings.Development.json. Mật khẩu shopapi_pw chỉ để thử trên máy, còn mật khẩu thật thì bài Cấu hình khuyên lưu bằng dotnet user-secrets:

"ConnectionStrings": {
  "Shop": "User Id=shopapi;Password=shopapi_pw;Data Source=localhost:1521/FREEPDB1"
}

Tạo DbContext và đăng ký vào Program.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
var cs = builder.Configuration
    .GetConnectionString("Shop");

builder.Services.AddControllers();
builder.Services.AddDbContext<ShopDbContext>(options =>
    options.UseOracle(cs)
        .UseUpperSnakeCaseNamingConvention());

var app = builder.Build();
app.MapControllers();
app.Run();

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public decimal Price { get; set; }
}

public class ShopDbContext : DbContext
{
    public ShopDbContext(
        DbContextOptions<ShopDbContext> options)
        : base(options)
    {
    }

    public DbSet<Product> Products => Set<Product>();
}

[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
    private readonly ShopDbContext _db;

    public ProductsController(ShopDbContext db)
    {
        _db = db;
    }

    [HttpGet]
    public async Task<List<Product>> GetAll() =>
        await _db.Products.ToListAsync();
}
  • UseOracle(cs) chọn Oracle, đọc chuỗi kết nối từ cấu hình.
  • Products => Set<Product>() là property chỉ đọc viết gọn bằng =>, trả về DbSet của bảng sản phẩm.
  • Để gọn, controller nhận thẳng ShopDbContext. Bài Truy vấn sẽ đặt nó sau một interface như bài Dependency injection.
  • UseUpperSnakeCaseNamingConvention() đổi tên sang chữ hoa nối gạch dưới: Products thành PRODUCTS, UnitPrice thành UNIT_PRICE. Tên khớp với cách viết của khoá SQL. Khoá chính theo quy ước EF Core tên là Id, nên cột là ID chứ không phải PRODUCT_ID.
  • AddDbContext đăng ký ShopDbContext với vòng đời Scoped, mỗi request một phiên làm việc.
  • ToListAsync() đọc cả bảng. Dùng bản Async vì đọc database là việc chậm.

Thử ngay

Chạy server với code trên, rồi gọi:

curl -i http://localhost:5000/api/products

Đoán trước khi chạy: code biên dịch được và server chạy bình thường. Lần gọi này trả về danh sách rỗng hay lỗi?

Xem kết quả
HTTP/1.1 500 Internal Server Error

Log của server:
ORA-00942: table or view "SHOPAPI"."PRODUCTS" does not exist

Lỗi 500. EF Core kết nối được Oracle, nhưng user shopapi chưa có bảng PRODUCTS, vì class C# không tự sinh ra bảng. Bài sau dùng migration để tạo bảng.

Lỗi hay gặp

Quên đăng ký DbContext. Controller cần ShopDbContext nhưng container không biết cách tạo, nên request trả 500.

// SAI — thiếu AddDbContext
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
// ĐÚNG
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<ShopDbContext>(options =>
    options.UseOracle(cs)
        .UseUpperSnakeCaseNamingConvention());
var app = builder.Build();

Bỏ UseUpperSnakeCaseNamingConvention(). EF Core với Oracle đặt tên bảng trong ngoặc kép, giữ nguyên chữ hoa chữ thường, ví dụ "Products". Viết SELECT * FROM products như khoá SQL sẽ báo ORA-00942, vì Oracle đổi products không có ngoặc kép thành PRODUCTS, khác với "Products".

Tóm tắt

  • EF Core là ORM: class thành bảng, object thành dòng.
  • DbContext là phiên làm việc với database, mỗi DbSet<T> là một bảng.
  • Kết nối Oracle bằng UseOracle, đặt tên chữ hoa bằng UseUpperSnakeCaseNamingConvention().
  • API dùng user riêng. Có class chưa đủ, phải tạo bảng trước khi dùng.

Tự kiểm tra

0/3 câu
Câu 1

DbContext có DbSet<Customer> Customers. Với UseUpperSnakeCaseNamingConvention, bảng trong Oracle tên là gì?

Câu 2

AddDbContext đăng ký DbContext với vòng đời nào?

Câu 3

Gọi API nhận 500, log báo ORA-00942 table or view "SHOPAPI"."ORDERS" does not exist. Nguyên nhân?

Middleware và pipelineMigration

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt