Ở bài trước mình đã giới thiệu với các bạn về UnitOfWork Design Pattern.
Nhắc lại vể lợi ích của UOW
- Quản lý danh sách các đối tượng logic nghiệp vụ được thay đổi trong một transaction.
- Khi một transaction được complete, tất cả thay đổi sẽ được gửi tới database như một
big unit of work
Bài toán:
Để hình dung rõ hơn, lần này chúng ta sẽ xem xét bài toán thực tế khi quản trị hệ thống muốn xóa một đơn hàng. Chúng ta cần xóa dữ liệu ở 2 bảng Orders và OrderDetails.
Theo cách làm thông thường, chúng ta sẽ xóa dữ liệu bảng OrderDetails trước, sau đó xóa dữ liệu bảng Orders. Với cách làm này rủi ro xảy ra nếu sau khi xóa dữ liệu bảng OrderDetails xong, một exception xảy ra (ví dụ kinh điển là mất mạng) lúc này dữ liệu bảng Orders chưa được xóa, dẫn tới việc không đảm bảo tính toàn vẹn dữ liệu và có thể gây lỗi hệ thống khi truy xuất đơn hàng.
Để giải quyết bài toán này. Chúng ta sử dụng pattern UnitOfWork, thực hiện việc xóa dữ liệu ở 2 bảng trong cùng 1 transaction. Bất kì ngoại lệ nào xảy ra, dữ liệu sẽ được rollback. Nếu thao tác xóa dữ liệu thành công, transaction sẽ được commit.
Đồ nghề:
Xây dựng hệ thống
Cùng xem xét lại quan điểm:
Simplicity is the ultimate sophistication
Áp dụng thực tế vào bài toán khi làm việc với Database, để tránh việc code thừa ở khắp nơi khi gọi query dữ liệu. Trong ví dụ này chúng ta dùng thêm repository pattern để xử lý tầng kiến trúc làm việc với database.

Với tư tưởng như trên, chúng ta xây dựng lớp Repository chuyên xử lý các thao tác thông thường với database. Lớp OrderRepository xử lý các vấn đề đặc thù của Domain Order
Cấu trúc dự án tổng thể như sau:

Đầu tiên chúng ta xây dựng interface IRepository
public interface IRepository<TEntity> where TEntity : class
{
TEntity Get(int id);
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void RemoveRange(IEnumerable<TEntity> entities);
}
Và lớp Repository implement interface IRepository
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
protected readonly DbSet<TEntity> DbSetEntity;
public Repository(DbContext context)
{
Context = context;
DbSetEntity = Context.Set<TEntity>();
}
public void Add(TEntity entity)
{
DbSetEntity.Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
DbSetEntity.AddRange(entities);
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return DbSetEntity.Where(predicate);
}
public TEntity Get(int id)
{
return DbSetEntity.Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return DbSetEntity.ToList();
}
public void Remove(TEntity entity)
{
DbSetEntity.Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
DbSetEntity.RemoveRange(entities);
}
public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return DbSetEntity.SingleOrDefault(predicate);
}
}
Interface IOrderRepository kế thừa từ IRepository
public interface IOrderRepository : IRepository<Orders>
{
IEnumerable<Orders> GetTopOrders(int count);
IEnumerable<Orders> GetOrdersWithCustomers(int pageIndex, int pageSize, string customerId);
}
Lớp OrderRepository kế thừa từ IOrderRepository và Repository
public class OrderRepository : Repository<Orders>, IOrderRepository
{
public OrderRepository(NorthwindContext context) : base(context)
{
}
public IEnumerable<Orders> GetOrdersWithCustomers(int pageIndex, int pageSize, string customerId)
{
return NorthwindContext.Orders
.Where(o => o.CustomerId == customerId)
.OrderBy(c => c.OrderId)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToList();
}
public IEnumerable<Orders> GetTopOrders(int count)
{
return NorthwindContext.Orders.OrderByDescending(c => c.OrderId).Take(count).ToList();
}
public NorthwindContext NorthwindContext
{
get { return Context as NorthwindContext; }
}
}
Tiếp theo chúng ta tạo interface IUnitOfWork
public interface IUnitOfWork : IDisposable
{
IOrderRepository Orders { get; }
ICustomerRepository Customers { get; }
IOrderDetailRepository OrderDetails { get; }
int Complete();
}
Và implement của nó là class UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly NorthwindContext _context;
public UnitOfWork(NorthwindContext context)
{
_context = context;
Orders = new OrderRepository(_context);
Customers = new CustomerRepository(_context);
OrderDetails = new OrderDetailRepository(_context);
}
public IOrderRepository Orders { get; set; }
public ICustomerRepository Customers { get; set; }
public IOrderDetailRepository OrderDetails { get; set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
Vậy là chúng ta đã implement xong mô hình của hệ thống, áp dụng 2 partern là Repository và UnitOfWork.
Tiếp theo chúng ta sẽ tới lớp Program và viết mã test hoạt động của hệ thống trong hàm Main
Class program sẽ trông như sau:
class Program
{
static void Main(string[] args)
{
using (var unitOfWork = new UnitOfWork(new NorthwindContext()))
{
// Example1
var customersByCity = unitOfWork.Customers.GetCustomerByCity("London");
// Example2
var customers = unitOfWork.Customers.GetAll();
// Example3
var order = unitOfWork.Orders.Get(10250);
var orderDetails = unitOfWork.OrderDetails.GetByOrderId(order.OrderId);
unitOfWork.OrderDetails.RemoveRange(orderDetails);
unitOfWork.Orders.Remove(order);
unitOfWork.Complete();
Console.WriteLine("Done!");
}
}
}