Repository, đọc một cách solo giản, là một tầng phân loại giữa Data Access Layer (DAL) với Bussiness logic Layer.Bạn đang xem: Unit of work là gì
Unit of Work là một kỹ thuật để đảm bảo an toàn tất cả các request tới database cơ mà có liên quan tới nhau hầu như được tiến hành trên cùng một DbContext
1. DbContext của Entity Framework Core2. Repository1. DbContext của Entity Framework Core“Trước giờ xài nhưng có lưu ý DbContext là gì đâu” -> đây chắc là tình trạng phổ biến của kha khá technology liên quan tới .NET, lúc mà phần nhiều thứ đang được xuất bản sẵn, và bạn chỉ có nhiệm vụ … xài
DbContext là một trong những thực thể đại diện thay mặt cho một phiên thao tác làm việc với database, dùng làm query với lưu tài liệu của bạn
Vì nó chỉ đại diện cho một phiên làm việc, trong ASP.NET, từng khi có một request bắt đầu từ browser, 1 DbContext mới sẽ tiến hành tạo ra, và sẽ bị dispose lúc return response đến browser
Thông thường, bạn sẽ kế quá lại từ bỏ DbContext, nhét thêm những DbSet vào, tự đó new query các kiểu được
chúng ta có thể tham khảo thêm về cách thiết lập cấu hình DbContext sống đây, mục 3.2
1.1. DbContext Tracking
Để bảo đảm tính toàn diện dữ liệu, DbContext cần sử dụng 1 lý lẽ gọi là tracking.
Bạn đang xem: Unit of work là gì
Cho tới khi bạn đã thực hiện toàn bộ các đổi khác cần thiết, rồi hotline yourDbContext.SaveChanges(), thì lúc này, những thay đổi được track sẽ được ship xuống database.
Vậy tất cả những vấn đề này liên quan gì cho tới Repository với Unit of Work? DbContext là 1 implement của Repository cùng Unit of Work, chỉ tất cả điều nó nằm sâu trong framework, còn implement của các bạn sẽ nằm ở application
2. RepositoryCó loại sơ trang bị hay ho sau

Bạn bao gồm thể tham khảo thêm về những services được inject tại bài viết Dependency Injection trong ASP.NET Core
Vậy đóa, Repository đóng vai trò là một trong những lớp trung gian giữa Bussiness xúc tích và ngắn gọn Layer (controllers cùng services) và Data Access Layer (các DbContext)
2.1. Lý do
Tách vấn đề xử lý xúc tích và việc truy vấn databaseDễ trace bugDễ unit testDễ chuyển đổi logic hoặc databaseGom tầm thường nhìu tác vụ cơ bản về 1 chỗKo phải viết đi viết lại 1 tác vụ các lần2.2. Implement
Cụ tỉ, Repository sẽ sở hữu được các nhiệm vụ: Liệt kê danh sách những record – lấy 1 record – Thêm – Xóa – Sửa 1 record
Bài toán: quản lý học sinh
Tạo interface
using System;using System.Collections.Generic;using ContosoUniversity.Models;namespace ContosoUniversity.DAL public interface IStudentRepository : IDisposable IEnumerable GetStudents(); Student GetStudentByID(int studentId); void InsertStudent(Student student); void DeleteStudent(int studentID); void UpdateStudent(Student student); void Save(); Đoạn code bên trên khai báo một bộ CRUD (Create – Read – Update – Delete) gớm điển
Tạo class implement
using System;using System.Collections.Generic;using System.Linq;using System.Data;using ContosoUniversity.Models;namespace ContosoUniversity.DAL public class StudentRepository : IStudentRepository, IDisposable // SchoolContext kế thừa từ DbContext, và tất cả thêm DbSet private SchoolContext context; public StudentRepository(SchoolContext context) this.context = context; public IEnumerable GetStudents() return context.Students.ToList(); public Student GetStudentByID(int id) return context.Students.Find(id); public void InsertStudent(Student student) context.Students.Add(student); public void DeleteStudent(int studentID) Student student = context.Students.Find(studentID); context.Students.Remove(student); public void UpdateStudent(Student student) context.Entry(student).State = EntityState.Modified; 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); GC.SuppressFinalize(this); Inject repository này vào Controller hoặc Service (nhớ khai báo nó trong Startup.cs trước nhóe)
public class StudentController private readonly IStudentRepository _studentRepository; public StudentController(IStudentRepository studentRepository) _studentRepository = studentRepository
2.3. Performance Hit
Entity Framework khi query một record hoặc 1 cỗ record nào kia trong database, nó sẽ trả về dạng IQueryable. Chỉ khi nào bạn điện thoại tư vấn .ToList();, thì câu lệnh SQL mới được có mặt và gửi đến database.Xem thêm: Fiancee Là Gì, Nghĩa Của Từ Fiancé, Từ Điển Anh Việt Fiancée
Trong StudentRepository sống trên, nếu bạn có nhu cầu filter 1 list những student mang tên là “ABC”, thì sẽ yêu cầu code trong controller như sau
public IActionResult GetStudents(string name) // SQL đã có được sinh ra, toàn bộ student vào db đã có được trả về và lưu vào memory var allStudents = _studentRepository.GetStudents(); // filter này chỉ triển khai việc filter trên memory var filteredStudents = allStudents.Where(x => x.Name.Contains(name)); return View(filteredStudents);Đây là một trong những code cực kỳ tệ khi mà student gồm hàng triệu record, trong khi bạn chỉ cần 1 số ít các record nhưng mà thôi.
Ở phần sau, bạn sẽ biết biện pháp fix cho vụ việc này, đồng thời implement 1 generic repository cho những tác vụ CRUD cơ bản
3. Generic RepositoryVề cơ bản, ta sẽ cần sử dụng kiểu khai báo generic class của C# để implement generic repository
using System;using System.Collections.Generic;using System.Linq;using System.Data;using System.Data.Entity;using ContosoUniversity.Models;using System.Linq.Expressions;namespace ContosoUniversity.DAL public class GenericRepository where TEntity : class // SchoolContext được kế thừa từ DbContext internal SchoolContext context; // Generic repository này sẽ vận động dựa trên entity được truyền vào khi đk trong Startup.cs internal DbSet dbSet; public GenericRepository(SchoolContext context) this.context = context; this.dbSet = context.Set(); // Expression> filter: chất nhận được bạn truyền vào một trong những filter expression dạng LINQ public virtual IEnumerable Get( Expression> filter = null, Func, IOrderedQueryable> orderBy = null, string includeProperties = "") IQueryable query = dbSet; // Query là 1 trong dạng IQueryable, chỉ được xúc tiến khi đề nghị giá trị menu if (filter != null) query = query.Where(filter); // Tiếp theo, nó vẫn kèm theo những property cần thiết khi người tiêu dùng chỉ định foreach (var includeProperty in includeProperties.Split (new char ',' , StringSplitOptions.RemoveEmptyEntries)) query = query.Include(includeProperty); // Sau cùng, nó thực thi bằng cách translate thành câu lệnh SQL và call xuống database if (orderBy != null) return orderBy(query).ToList(); else return query.ToList(); // vào asp.net, Id cho 1 object hoàn toàn có thể là GUID hoặc int public virtual TEntity GetByID(object id) return dbSet.Find(id); public virtual void Insert(TEntity entity) dbSet.Add(entity); // trong asp.net, Id cho một object có thể là GUID hoặc int public virtual void Delete(object id) TEntity entityToDelete = dbSet.Find(id); Delete(entityToDelete); public virtual void Delete(TEntity entityToDelete) if (context.Entry(entityToDelete).State == EntityState.Detached) dbSet.Attach(entityToDelete); dbSet.Remove(entityToDelete); public virtual void Update(TEntity entityToUpdate) dbSet.Attach(entityToUpdate); context.Entry(entityToUpdate).State = EntityState.Modified; 4. Tạo nên class Unit of WorkUnit of Work chỉ có một nhiệm vụ duy nhất, đảm bảo tất cả những repository của người tiêu dùng đều dùng bình thường một DbContext. Bằng cách này, khi thực hiện hoàn thành tất cả những tác vụ thay đổi database, bạn chỉ cần gọi DbContext.SaveChanges() 1 lần duy nhất, với các đổi khác đó sẽ tiến hành lưu lại trong database
using System;using ContosoUniversity.Models;namespace ContosoUniversity.DAL public class UnitOfWork : IDisposable private SchoolContext context = new SchoolContext(); private GenericRepository departmentRepository; private GenericRepository courseRepository; // soát sổ xem repository đã có khởi sinh sản chưa public GenericRepository DepartmentRepository get if (this.departmentRepository == null) this.departmentRepository = new GenericRepository(context); return departmentRepository; // bình chọn xem repository đã được khởi chế tạo chưa public GenericRepository CourseRepository get if (this.courseRepository == null) this.courseRepository = new GenericRepository(context); return courseRepository; 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); GC.SuppressFinalize(this); Bước tiếp theo sau là chuyển đổi code của controller để áp dụng class UnitOfWork vừa bắt đầu khởi tạo
// rước datavar courses = unitOfWork.CourseRepository.Get(includeProperties: "Department");// Lấy và order datavar departmentsQuery = unitOfWork.DepartmentRepository.Get(orderBy: q => q.OrderBy(d => d.Name));// Insertvar course = new Course();course.Name = "Test";...unitOfWork.CourseRepository.Insert(course);unitOfWork.Save();// HủyunitOfWork.Dispose();5. Tổng kếtVậy là bạn đã hiểu quan niệm và cách khai báo Repository với Unit of Work pattern. Chúng ta cũng đã biết cách sử dụng lambda expression để query các data thỏa điều kiện mong muốn trải qua interface IQueryable. Chúc vui