+3

Unit Of Work với Repository Pattern - C#

Unit of work trong Mẫu Repository Pattern với Ví dụ

Trong bài viết này, tôi sẽ thảo luận về cách thực hiện Đơn vị Công việc (Unit Of Work) trong Mẫu Repository Pattern trong C# với các Ví dụ. Đơn vị công việc trong C# quản lý các hoạt động CRUD trên cơ sở dữ liệu trong bộ nhớ như một giao dịch. Nói một cách đơn giản, chúng ta có thể nói rằng nếu chúng ta muốn triển khai Giao dịch khi sử dụng Entity Framework và Mẫu Thiết kế Repository, thì chúng ta cần sử dụng đơn vị công việc. Vì vậy, nếu một trong những hoạt động thất bại như một phần của giao dịch, tất cả các hoạt động cơ sở dữ liệu sẽ được quay trở lại. Điều đó có nghĩa là hoặc tất cả các hoạt động cơ sở dữ liệu thành công hoặc không có hoạt động nào.

Đơn vị công việc trong C# là một khái niệm liên quan đến việc thực hiện hiệu quả Mẫu Thiết kế Repository. Vì vậy, để hiểu khái niệm này, quan trọng là phải hiểu khái niệm Mẫu Thiết kế Repository trong C#.

Repository là gì?

Như đã thảo luận trước đó, repository là một lớp được xác định cho một thực thể với tất cả các hoạt động cơ sở dữ liệu có thể có. Ví dụ, một repository cho một thực thể Nhân viên sẽ có các hoạt động CRUD cơ bản và bất kỳ hoạt động khác nào có thể liên quan đến thực thể Nhân viên. Mẫu Repository có thể được triển khai theo hai cách, tức là sử dụng Một Repository Cho Mỗi Thực Thể và Một Repository Cho Tất Cả Thực Thể. Cũng có thể bao gồm cả hai trong ứng dụng của chúng ta.

Một Repository Cho Mỗi Thực Thể (Repository Không Generic):

Trong trường hợp này, chúng ta phải tạo một repository riêng cho mỗi thực thể. Ví dụ, nếu chúng ta có hai thực thể, Nhân viên và Khách hàng, trong ứng dụng của chúng ta, chúng ta phải tạo hai repository. Repository Nhân viên sẽ có các hoạt động liên quan đến Thực thể Nhân viên và Repository Khách hàng sẽ chỉ có các hoạt động liên quan đến Thực thể Khách hàng.

Repository Generic (Một Repository Cho Tất Cả Thực Thể):

Một Repository Generic có thể được sử dụng cho tất cả các thực thể. Nói cách khác, Repository Generic có thể được sử dụng cho Thực thể Nhân viên, Thực thể Khách hàng hoặc bất kỳ thực thể nào khác. Vì vậy, tất cả các hoạt động tiêu chuẩn của tất cả các thực thể sẽ được đặt trong Repository Generic.

Lưu Ý: Chúng ta sử dụng cả Repository Generic và Không Generic trong hầu hết Ứng dụng Thời Gian Thực. Repository Generic chứa các phương thức chung cho tất cả các thực thể. Nhưng nếu bạn muốn một số hoạt động cụ thể cho một số thực thể cụ thể. Sau đó, bạn cần tạo một repository cụ thể với các hoạt động cần thiết.

Unit of work trong Mẫu Repository Pattern

Mẫu Đơn vị Công việc trong C# nhóm một hoặc nhiều hoạt động (thường là hoạt động CRUD cơ sở dữ liệu) vào một giao dịch duy nhất và thực hiện chúng bằng cách áp dụng nguyên tắc làm tất cả hoặc không làm gì cả. Điều đó có nghĩa là nếu bất kỳ hoạt động nào của giao dịch thất bại, nó sẽ quay trở lại giao dịch. Nếu tất cả các hoạt động đều thành công, thì nó sẽ xác nhận giao dịch. Vì vậy, nó sẽ thực hiện tất cả các hoạt động cơ sở dữ liệu như một đơn vị.

Chúng ta sẽ làm việc với cùng một ví dụ mà chúng ta đã sử dụng trong bài viết trước của chúng tôi. Dưới đây là đoạn mã cho lớp Repository Generic.

// Đoạn mã cho lớp Repository Generic
using RepositoryUsingEFinMVC.DAL;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;

namespace RepositoryUsingEFinMVC.GenericRepository
{
    public class GenericRepository<T> : IGenericRepository<T> where T : class
    {
        public EmployeeDBContext _context = null;
        public DbSet<T> table = null;

        public GenericRepository()
        {
            this._context = new EmployeeDBContext();
            table = _context.Set<T>();
        }

        public GenericRepository(EmployeeDBContext _context)
        {
            this._context = _context;
            table = _context.Set<T>();
        }

        public IEnumerable<T> GetAll()
        {
            return table.ToList();
        }

        public T GetById(object id)
        {
            return table.Find(id);
        }

        public void Insert(T obj)
        {
            table.Add(obj);
        }

        public void Update(T obj)
        {
            table.Attach(obj);
            _context.Entry(obj).State = EntityState.Modified;
        }

        public void Delete(object id)
        {
            T existing = table.Find(id);
            table.Remove(existing);
        }

        public void Save()
        {
            _context.SaveChanges();
        }
    }
}

Vấn đề với mã Triển khai trên:

Vấn đề phát sinh khi chúng ta làm việc với nhiều repository. Ví dụ, nếu chúng ta làm việc với hai repository, hãy nói Nhân viên và Sản phẩm, thì trong trường hợp này, cả hai repository sẽ tạo ra và duy trì một phiên bản riêng của lớp DbContext. Điều này có thể dẫn đến vấn đề trong tương lai vì mỗi đối tượng DbContext sẽ có danh sách thay đổi trong bộ nhớ riêng của nó, tức là duy trì trạng thái của các thực thể, tức là Thêm / Sửa đổi / Xóa. Trong trường hợp như vậy, nếu phương thức SaveChanges của một trong các repository thất bại và phương thức SaveChanges của một trong các repository khác thành công, điều này sẽ dẫn đến sự không nhất quán trong cơ sở dữ liệu. Trong ví dụ của chúng tôi, trong khi thêm dữ liệu cho các thực thể Nhân viên và Sản phẩm, cả hai đều sử dụng một phiên bản riêng của lớp DbContext. Vì vậy, nếu phương thức SaveChanges của repository Nhân viên thất bại, nhưng phương thức SaveChanges của repository Sản phẩm thành công, dữ liệu Sản phẩm sẽ được thêm vào cơ sở dữ liệu nhưng dữ liệu Nhân viên sẽ không được thêm vào cơ sở dữ liệu.

Để giải quyết vấn đề này, chúng ta cần sử dụng đơn vị công việc.

Triển Khai Đơn vị Công việc: Đơn vị công việc sẽ duy trì một phiên bản của lớp DbContext trong một ứng dụng và sẽ sử dụng nó để thực hiện các hoạt động của cơ sở dữ liệu cho tất cả các repository trong ứng dụng. Điều này đảm bảo rằng tất cả các repository sẽ làm việc với cùng một phiên bản của DbContext và do đó, tất cả các hoạt động cơ sở dữ liệu sẽ được thực hiện trong một giao dịch. Điều này đảm bảo rằng nếu bất kỳ hoạt động nào của giao dịch thất bại, nó sẽ quay trở lại giao dịch. Nếu tất cả các hoạt động đều thành công, thì nó sẽ xác nhận giao dịch.

Dưới đây là mã cho lớp Unit of work.

using RepositoryUsingEFinMVC.DAL;

namespace RepositoryUsingEFinMVC.UnitOfWork
{
    public class UnitOfWork : IDisposable
    {
        private EmployeeDBContext _context = new EmployeeDBContext();
        private GenericRepository<Employee> employeeRepository;
        private GenericRepository<Product> productRepository;

        public GenericRepository<Employee> EmployeeRepository
        {
            get
            {
                if (this.employeeRepository == null)
                    this.employeeRepository = new GenericRepository<Employee>(_context);
                return employeeRepository;
            }
        }

        public GenericRepository<Product> ProductRepository
        {
            get
            {
                if (this.productRepository == null)
                    this.productRepository = new GenericRepository<Product>(_context);
                return productRepository;
            }
        }

        public void Save()
        {
            _context.SaveChanges();
        }

        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            this.disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }
    }
}

Như bạn có thể thấy trong đoạn mã trên, lớp UnitOfWork sử dụng lớp DbContext (_context) để tạo ra các repository và thực hiện hoạt động cơ sở dữ liệu. Lớp UnitOfWork này sẽ đảm bảo rằng tất cả các repository trong ứng dụng sẽ làm việc với cùng một phiên bản của DbContext và do đó, tất cả các hoạt động cơ sở dữ liệu sẽ được thực hiện trong một giao dịch.

Kết luận:

Trong bài viết này, chúng ta đã thảo luận về cách thực hiện Đơn vị Công việc (Unit Of Work) trong Mẫu Repository Pattern trong C# với các Ví dụ. Như bạn đã thấy, Đơn vị Công việc trong C# nhóm một hoặc nhiều hoạt động (thường là hoạt động CRUD cơ sở dữ liệu) vào một giao dịch duy nhất và thực hiện chúng bằng cách áp dụng nguyên tắc làm tất cả hoặc không làm gì cả. Điều đó có nghĩa là nếu bất kỳ hoạt động nào của giao dịch thất bại, nó sẽ quay trở lại giao dịch. Nếu tất cả các hoạt động đều thành công, thì nó sẽ xác nhận giao dịch. Vì vậy, nó sẽ thực hiện tất cả các hoạt động cơ sở dữ liệu như một đơn vị.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí