So sánh ADO.NET và Dapper trong C#
Tổng quan
Không có lựa chọn "tốt nhất" tuyệt đối, mà phụ thuộc vào bối cảnh. Đối với hầu hết các dự án kinh doanh, Dapper thường hiệu quả hơn; còn đối với các trường hợp cần kiểm soát cao hoặc phát triển lớp cơ sở hạ tầng, ADO.NET vẫn không thể thay thế.
Mối quan hệ bản chất
Dapper là một kết nối ORM nhẹ dựa trên ADO.NET, chứ không phải là sự thay thế. Nó sử dụng các đối tượng như IDbConnection, IDbCommand... từ ADO.NET ở phía dưới, nhưng cung cấp phân mảnh đối tượng và gắn kết tham số.
Mã của bạn → Dapper → ADO.NET → Driver cơ sở dữ liệu → Cơ sở dữ liệu
So sánh từng khía cạnh
| Yếu tố | ADO.NET | Dapper |
|---|---|---|
| Số lượng mã | Nhiều, cần mở đóng kết nối thủ công, ánh xạ trường | Ít, chỉ cần một dòng truy vấn + ánh xạ |
| Hiệu suất | Tốc độ nhanh nhất (không chi phí) | Gần như nguyên bản, chênh lệch micro giây có thể bỏ qua |
| Chi phí học tập | Cao, cần hiểu Command/Reader/Adapter... | Thấp, chỉ cần viết SQL là có thể bắt đầu |
| Khả năng kiểm soát SQL | Kiểm soát hoàn toàn | Kiểm soát hoàn toàn (viết SQL thủ công) |
| Ánh xạ đối tượng | Ánh xạ thủ công reader["Name"] |
Ánh xạ tự động sang POCO |
| Xử lý tham số | Tạo SqlParameter thủ công |
Đối tượng ẩn danh/động được mở rộng tự động |
| Quy trình lưu trữ | Hỗ trợ nguyên bản nhưng mã dài dòng | Hỗ trợ nguyên bản, mã ngắn gọn |
| Giao dịch | Quản lý thủ công | API đơn giản tích hợp sẵn |
| Thao tác hàng loạt | Cần tự thực hiện | Sử dụng Execute + giao dịch, ngắn gọn |
| Nhiều kết quả | Xử lý thủ công bằng NextResult() |
Xử lý đẹp mắt bằng QueryMultiple |
| Phụ thuộc | Có sẵn trong .NET, không phụ thuộc | Gói NuGet (khoảng 200KB) |
| Khó khăn khi gỡ lỗi | Thấp, logic minh bạch | Thấp, SQL và tham số có thể ghi nhật ký |
| Mức độ áp dụng | Lớp cơ sở/đầu vào | Lớp ứng dụng kinh doanh |
So sánh trực quan về mã
Truy vấn một bản ghi
ADO.NET:
using var conn = new SqlConnection(connStr);
conn.Open();
using var cmd = new SqlCommand("SELECT Id, Name, Age FROM Users WHERE Id = @Id", conn);
cmd.Parameters.AddWithValue("@Id", 1);
using var reader = cmd.ExecuteReader();
if (reader.Read())
{
var user = new User
{
Id = (int)reader["Id"],
Name = (string)reader["Name"],
Age = (int)reader["Age"]
};
}
Dapper:
using var conn = new SqlConnection(connStr);
var user = conn.QueryFirstOrDefault<User>(
"SELECT Id, Name, Age FROM Users WHERE Id = @Id",
new { Id = 1 });
Rõ ràng ngay lập tức — ADO.NET cần 12+ dòng để hoàn thành, Dapper chỉ cần 3 dòng.
Chèn hàng loạt
ADO.NET:
using var conn = new SqlConnection(connStr);
conn.Open();
using var tx = conn.BeginTransaction();
try
{
foreach (var user in users)
{
using var cmd = new SqlCommand(
"INSERT INTO Users(Name,Age) VALUES(@Name,@Age)", conn, tx);
cmd.Parameters.AddWithValue("@Name", user.Name);
cmd.Parameters.AddWithValue("@Age", user.Age);
cmd.ExecuteNonQuery();
}
tx.Commit();
}
catch { tx.Rollback(); throw; }
Dapper:
using var conn = new SqlConnection(connStr);
conn.Execute(
"INSERT INTO Users(Name,Age) VALUES(@Name,@Age)",
users, transaction: conn.BeginTransaction());
Tham khảo hiệu suất
Trên cơ sở BenchmarkDotNet cho các tình huống truy vấn (100.000 lần ánh xạ):
| Hành động | ADO.NET | Dapper | Chênh lệch |
|---|---|---|---|
| Truy vấn đơn giản | ~1,8ms | ~2,1ms | +17% |
| Truy vấn có tham số | ~2,0ms | ~2,3ms | +15% |
| Chèn hàng loạt (1000 bản ghi) | ~45ms | ~47ms | +4% |
Chi phí hiệu suất của Dapper chủ yếu đến từ phản chiếu (sau lần gọi đầu tiên sẽ được lưu cache), và các lần gọi sau gần như không tốn thêm chi phí. Trong môi trường kinh doanh thực tế, I/O cơ sở dữ liệu mới là điểm nghẽn, khoảng cách này có thể bỏ qua.
Lựa chọn theo tình huống
| Tình huống | Đề xuất | Lý do |
|---|---|---|
| Dự án kinh doanh thông thường | Dapper | Hiệu suất phát triển cao, mã ngắn gọn, chi phí bảo trì thấp |
| Dự án microservices/API | Dapper | Kết hợp với mô hình Repository, phát triển nhanh chóng |
| Phát triển driver/cung cấp cơ sở dữ liệu | ADO.NET | Cần thực hiện giao diện nền tảng, không thể phụ thuộc vào lớp trên |
| Cảnh huống hiệu suất tối đa (giao dịch tần suất cao) | ADO.NET | Mỗi micro giây đều quan trọng, loại bỏ chi phí ánh xạ |
| Học hiểu nguyên lý cơ sở dữ liệu | ADO.NET | Hiểu về quản lý kết nối, thực thi lệnh, giao dịch... |
| Dự án cũ đã có nhiều mã ADO.NET | Cả hai đều có thể | Có thể dần đưa Dapper vào, không cần viết lại |
| Cần ánh xạ phức tạp (truy vấn nhiều bảng → đối tượng lồng nhau) | Dapper | Hỗ trợ Query<T1,T2,T3> rất hữu ích |
Làm rõ những hiểu lầm phổ biến
- "Dapper không an toàn" — Sai. Dapper sử dụng truy vấn có tham số, phòng chống SQL injection giống ADO.NET.
- "Dapper chậm" — Sai. Dapper là ORM nhỏ nhất với hiệu suất tốt nhất, gần như nguyên bản ADO.NET.
- "Dùng Dapper thì không cần hiểu ADO.NET" — Sai. Các khái niệm như quản lý kết nối, giao dịch, thời gian chờ của Dapper đều đến từ ADO.NET, hiểu nền tảng mới có thể dùng tốt lớp trên.
- "Dapper không thể dùng stored procedure" — Sai. Chỉ cần thiết lập
commandType: CommandType.StoredProcedure.
Kết luận: Đối với 90% các tình huống kinh doanh, Dapper là lựa chọn ưu việt; 10% còn lại liên quan đến lớp cơ sở, hiệu suất tối đa hoặc mục đích học tập, hãy sử dụng ADO.NET nguyên bản. Hai công cụ này không đối lập, Dapper là sự bổ sung cho ADO.NET chứ không phải thay thế.