Giới Thiệu Về Mẫu Unit Of Work
Mẫu Unit Of Work là một mẫu thiết kế dùng để nhóm nhiều thao tác (thường là các gọi đến repository) trong một giao dịch duy nhất. Mẫu này đảm bảo rằng hoặc tất cả các thao tác thành công hoặc không có thao tác nào được áp dụng, từ đó duy trì tính nhất quán của dữ liệu.
Mẫu này thường hoạt động song song với mẫu Repository, mẫu này trừu tượng hóa logic truy cập dữ liệu. Việc kết hợp cả hai mẫu này giúp quản lý các thao tác dữ liệu một cách hiệu quả hơn.
Thiết lập Các Repository Đầu Tiên
Định Nghĩa Các Thực Thể
csharp
// Models/Product.cs
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// Models/Order.cs
public class Order
{
public int Id { get; set; }
public int ProductId { get; set; }
public DateTime OrderDate { get; set; }
}
// Models/Log.cs
public class Log
{
public int Id { get; set; }
public string Message { get; set; }
public DateTime Timestamp { get; set; }
}
Thiết lập DbContext
csharp
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Log> Logs { get; set; }
}
Định Nghĩa Các Repository
csharp
public interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task AddAsync(Product product);
void Update(Product product);
void Delete(Product product);
}
public interface IOrderRepository
{
Task AddAsync(Order order);
}
public interface ILogRepository
{
Task AddAsync(Log log);
}
Triển Khai Các Repository
csharp
// Product repositories
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products.FindAsync(id);
}
public async Task AddAsync(Product product)
{
await _context.Products.AddAsync(product);
}
public void Update(Product product)
{
_context.Products.Update(product);
}
public void Delete(Product product)
{
_context.Products.Remove(product);
}
}
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context)
{
_context = context;
}
public async Task AddAsync(Order order)
{
await _context.Orders.AddAsync(order);
}
}
public class LogRepository : ILogRepository
{
private readonly AppDbContext _context;
public LogRepository(AppDbContext context)
{
_context = context;
}
public async Task AddAsync(Log log)
{
await _context.Logs.AddAsync(log);
}
}
Giao Diện & Triển Khai Mẫu Unit of Work
csharp
public interface IUnitOfWork : IDisposable
{
IProductRepository Products { get; }
IOrderRepository Orders { get; }
ILogRepository Logs { get; }
Task<int> CompleteAsync();
}
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public IProductRepository Products { get; }
public IOrderRepository Orders { get; }
public ILogRepository Logs { get; }
public UnitOfWork(
AppDbContext context,
IProductRepository productRepository,
IOrderRepository orderRepository,
ILogRepository logRepository)
{
_context = context;
Products = productRepository;
Orders = orderRepository;
Logs = logRepository;
}
public async Task<int> CompleteAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
Tiêm Các Phụ Thuộc
Hãy đảm bảo bạn đăng ký các phụ thuộc với phương thức AddScoped, để tất cả các repository sẽ chia sẻ cùng một instance của DbContext trong một cuộc gọi http cụ thể.
csharp
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<ILogRepository, LogRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
Thực Hiện Trong Controller
csharp
[ApiController]
[Route("api/[controller]")]
public class CheckoutController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
public CheckoutController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[HttpPost]
public async Task<IActionResult> Checkout([FromBody] ProductOrderDto dto)
{
var product = new Product { Name = dto.ProductName, Price = dto.Price };
var order = new Order { ProductId = product.Id, OrderDate = DateTime.UtcNow };
var log = new Log { Message = "New order placed", Timestamp = DateTime.UtcNow };
await _unitOfWork.Products.AddAsync(product);
await _unitOfWork.Orders.AddAsync(order);
await _unitOfWork.Logs.AddAsync(log);
// Giao dịch đơn
await _unitOfWork.CompleteAsync();
return Ok("Đặt hàng thành công.");
}
}
Những Điều Cần Lưu Ý
- Kiểm tra lỗi: Khi thực hiện các thao tác với cơ sở dữ liệu, bạn nên xử lý các ngoại lệ có thể xảy ra để đảm bảo ứng dụng không bị gặp sự cố.
- Hiệu suất: Đảm bảo rằng các thao tác được thực hiện một cách hiệu quả, đặc biệt trong các giao dịch lớn.
Kết Luận
Chúc mừng bạn! Bạn đã triển khai thành công mẫu Unit Of Work, từ đó giúp bảo vệ cơ sở dữ liệu của bạn khỏi các trạng thái không nhất quán. Hãy thử nghiệm và cho tôi biết nếu bạn có bất kỳ câu hỏi nào. Bạn có thể liên hệ với tôi qua LinkedIn để thảo luận thêm.