EF Core trong WinForms
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Tới giờ danh sách sản phẩm chỉ nằm trong bộ nhớ, tắt app là mất. Bài này
nối app với database shopapi mà API của khoá ASP.NET Core đang dùng. Hai app
cùng đọc ghi một bảng PRODUCTS.
Khái niệm
🧾 DbContextOptionsBuilder: class dựng cấu hình cho DbContext, gồm loại database, chuỗi kết nối và quy ước đặt tên.
Ở khoá ASP.NET Core, AddDbContext dựng cấu hình này và container tạo
ShopDbContext cho controller. WinForms không có container sẵn, nên ta tự
dựng cấu hình rồi tự new, như cách truyền phụ thuộc bằng tay ở bài DIP và
dependency injection của khoá OOP.
Ví dụ
Cài hai package như khoá ASP.NET Core:
dotnet add package Oracle.EntityFrameworkCore
dotnet add package EFCore.NamingConventionsCác bảng PRODUCTS (có cột STOCK), ORDERS, ORDER_LINES đã được
migration của API tạo ở chương EF Core, app này chỉ đọc ghi. Thay toàn bộ
Program.cs:
using Microsoft.EntityFrameworkCore;
namespace ShopDesk;
static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
var cs = "User Id=shopapi;Password=shopapi_pw;"
+ "Data Source=localhost:1521/FREEPDB1";
var options =
new DbContextOptionsBuilder<ShopDbContext>()
.UseOracle(cs)
.UseUpperSnakeCaseNamingConvention()
.Options;
var db = new ShopDbContext(options);
Application.Run(new MainForm(db));
}
}
class MainForm : Form
{
private readonly ShopDbContext _db;
private readonly BindingSource _source =
new BindingSource();
public MainForm(ShopDbContext db)
{
_db = db;
Text = "Kho hàng";
Width = 500;
var grid = new DataGridView
{
Dock = DockStyle.Fill,
DataSource = _source,
ReadOnly = true,
AllowUserToAddRows = false
};
Controls.Add(grid);
Load += MainForm_Load;
}
private void MainForm_Load(
object? sender, EventArgs e)
{
_source.DataSource = _db.Products.ToList();
}
}
public class ShopDbContext : DbContext
{
public ShopDbContext(
DbContextOptions<ShopDbContext> options)
: base(options)
{
}
public DbSet<Product> Products => Set<Product>();
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
public int Stock { get; set; }
}ShopDbContextvàProductchép nguyên từ khoá ASP.NET Core. Cùng class, cùng quy ước tên nên khớp đúng bảngPRODUCTScó sẵn.Mainlà nơi ghép các phần: dựngoptions, tạodb, rồi đưa vào constructor củaMainForm. Form không tự tạoShopDbContextmà nhận từ bên ngoài.- Mật khẩu viết thẳng trong code là trái với bài Cấu hình của khoá ASP.NET Core. Ở đây chỉ để thử trên máy, bài Đóng gói ứng dụng sẽ nói lại.
- Ở khoá ASP.NET Core mỗi request có một
DbContextriêng. Ở đây mộtDbContextsống suốt vòng đời của form, nên nó nhớ các dòng đã đọc. - Event
Loadchạy ngay trước khi cửa sổ hiện lần đầu, hợp để đọc dữ liệu.
Thử ngay
Bảng PRODUCTS của shopapi đã có Bút bi, Vở, Thước thêm ở khoá ASP.NET
Core. Trong VS Code, tạo kết nối với user shopapi, mật khẩu shopapi_pw,
service name FREEPDB1, rồi chạy câu sau nhưng chưa COMMIT:
INSERT INTO PRODUCTS (NAME, PRICE, STOCK)
VALUES ('Balo', 350000, 8);Chạy app bằng dotnet run.
Đoán trước khi chạy: lưới có "Balo" không? Nếu chưa có, cần làm gì để nó hiện?
Xem kết quả
Lưới có Bút bi, Vở, Thước, không có Balo.
Chạy COMMIT trong VS Code, đóng app rồi mở lại: Balo hiện.App mở một kết nối riêng tới Oracle. Như bài Transaction của khoá SQL, thay
đổi chưa COMMIT chỉ phiên thực hiện nó mới thấy.
Lỗi hay gặp
Quên UseUpperSnakeCaseNamingConvention(). EF Core tìm bảng
"Products" thay vì PRODUCTS. Mở app là hiện hộp thoại lỗi ORA-00942,
cùng mã lỗi đã gặp ở bài EF Core và DbContext của khoá ASP.NET Core.
// SAI — tìm bảng "Products", không có
var options =
new DbContextOptionsBuilder<ShopDbContext>()
.UseOracle("...")
.Options;// ĐÚNG — cùng quy ước với API
var options =
new DbContextOptionsBuilder<ShopDbContext>()
.UseOracle("...")
.UseUpperSnakeCaseNamingConvention()
.Options;Tóm tắt
- App WinForms dùng lại
ShopDbContext,Productvà database của API. - Tự dựng
optionsbằngDbContextOptionsBuilder, tự tạoShopDbContexttrongMain. - Form nhận
ShopDbContextqua constructor. - Đọc dữ liệu trong event
Load. App chỉ thấy dữ liệu đãCOMMIT.
Tự kiểm tra
0/3 câuTrong ASP.NET Core, AddDbContext làm hộ việc gì mà trong WinForms ta phải tự làm?
App đang mở thì đồng nghiệp đổi giá Bút bi và COMMIT. App gọi lại _db.Products.ToList() trên cùng _db. Lưới hiện giá nào?
Muốn app kho chạy với Oracle trên máy chủ thử nghiệm thay vì localhost. Sửa ở đâu?