© 2020, Developed by Hieu Dev

Tạo CRUD Web API bằng cách triển khai Repository Pattern trong .NET 5

Trong bài viết này, chúng ta sẽ tìm hiểu về vấn đề thường gặp trong .NET Core, đó là cách triển khai CRUD Web API bằng cách dùng repository pattern. Vậy nó là gì? cách tạo như thế nào? Hãy tìm hiểu bài viết sau đây, kèm theo video tutorial step by step để tiện việc tìm hiểu.

Tạo CRUD Web API bằng cách triển khai Repository Pattern trong .NET 5

Repository Pattern

Repository là một lớp trung gian giữa hai tầng business (Business Service Layer) và tầng truy xuất dữ liệu (DAL). Trong project sử dụng Entity Framework với ASP.NET MVC thì Data chính là tầng chứa các lớp DbContext và class entity. Còn business logic chính là tầng xử lý nghiệp vụ của dự án.

Repository hầu hết được sử dụng những chỗ cần điều chỉnh dữ liệu trước khi truyền xuống tầng data hoặc truyền lên trên business logic.

Bài viết này đã có video tutorial, các bạn có thể xem dưới đây:


Structure

Theo như video, mình sẽ tạo project theo kiến trúc như sau, bạn có thể tham khảo qua hình bên dưới:
Tạo CRUD Web API bằng cách triển khai Repository Pattern trong .NET 5

Như ảnh trên, thì các folder được tổ chức với ý nghĩa như sau:
  • AppDbContext: Để chứa các DbContext, Extension để seeding dữ liệu mẫu,...
  • Bussiness: Lớp này để chứa các Repository hoặc Services,...
  • Controllers: Tạo ra các API controller, bằng cách dùng các repository được định nghĩa từ Bussiness.
  • Entities: Chứa các entity của toàn project của chúng ta.
  • Infrastructure: Tạo ra các interface repository.
  • ViewModel: Chứa các data request để các tầng bussiness hay controller xử lý.

Code First

Việc đầu tiên cần làm là tạo ra các entities, dbcontext, để migration qua database của chúng ta (cụ thể là SQL Server).

Bước 1: Trước tiên, ta cài ba Nuget Packages sau:
  • Microsoft.EntityFrameworkCore 
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
Bước 2: Sau khi cài xong, ta lần lượt tạo các class entity sau trong folder Entities:

Category.cs
    public class Category
    {
        public int CategoryId { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Product> Products { get; set; }
    }


Product.cs
    public class Product
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Image { get; set; }
        public string Price { get; set; }
        public string Warranty { get; set; }
        public string Detail { get; set; }
        public int CategoryId { get; set; }

        public virtual Category Category { get; set; }
    }

Bước 3: Cấu hình chuỗi kết nối tới SQL Server trong appsetting.json:

"ConnectionStrings": {
    "ConnectionString": "Server=CNPM-PC\\SQLEXPRESS;Database=DbProductDemo;Trusted_Connection=True;MultipleActiveResultSets=true"
  },

Bước 4: Trong folder AppDbContext, ta tạo class DataDbContext.cs với code sau:

    public class DataDbContext : DbContext
    {
        public DataDbContext(DbContextOptions<DataDbContext> options)
               : base(options)
        {

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Đặt khóa chính
            modelBuilder.Entity<Product>().HasKey(s => s.Id);
            modelBuilder.Entity<Category>().HasKey(s => s.CategoryId);

            // ER
            modelBuilder.Entity<Product>()
                .HasOne(s => s.Category)
                .WithMany(s => s.Products)
                .HasForeignKey(s => s.CategoryId)
                .OnDelete(DeleteBehavior.Restrict);
        }

            public DbSet<Product> Products { get; set; }
            public DbSet<Category> Categories { get; set; }
    
    }

Bước 5: Ta vào Startup.cs và tìm đến phương thức ConfigureServices() và thêm đoạn code sau bên trong:
services.AddDbContext<DataDbContext>(options =>
                        options.UseSqlServer(
                            Configuration.GetConnectionString("ConnectionString")));

Bước 6: Vào Tools > NuGet Package Manager > NuGet Package Console và thực thi lần lượt 2 command sau:
  • Add-Migration InitialCreate
  • Update-database

Bước 7: Hoàn tất. Bây giờ ta có thể vào database để kiểm tra xem các entities đã sinh ra hay chưa. Với tên db được config ở bước 3:

CRUD Web API với Repository Pattern 

Tiếp theo với project, ta lần lượt theo các bước sau đây:

Bước 1: Trong thư mục ViewModel, ta tạo 2 class sau:
CreateCategoryVm.cs
    public class CreateCategoryVm
    {
        public string Name { get; set; }
    }

UpdateCategoryVm.cs
    public class UpdateCategoryVm
    {
        public int CategoryId { get; set; }
        public string Name { get; set; }
    }


Nếu như chúng ta không dùng các VM này, thay vì dùng luôn entity Category thì khi thực hiện các request rất lộn xộn, và không kiểm soát được dữ liệu nào cần create, dữ liệu nào define sẵn ở hệ thống.

Bước 2: Trong thư mục Infrastructure tạo interface ICategoryRepository.cs với code sau:

    public interface ICategoryRepository
    {
        public void Create(CreateCategoryVm categoryVm);

        public void Update(UpdateCategoryVm categoryVm);

        public IEnumerable<Category> GetAll();

        public Category GetById(int id);

        public void Delete(Category product);
    }

Các interface này là best practice để bạn ôn lại OOP trong .NET, về việc tạo ra các interface để ta define ra các chức năng chung, cụ thể ở trên là các phương thức implement theo các class action.

Bước 3: Trong thư mục Bussiness ta tạo class repository với tên CategoryRepository.cs với code sau:
    public class CategoryRepository : ICategoryRepository
    {
        private readonly DataDbContext _context;

        public CategoryRepository(DataDbContext context)
        {
            _context = context;
        }

        public IEnumerable<Category> GetAll()
        {
            return _context.Categories.ToList();
        }

        public Category GetById(int id)
        {
            return _context.Categories.FirstOrDefault(c => c.CategoryId == id);
        }

        public void Create (CreateCategoryVm categoryVm)
        {
            Category category = new Category()
            {
                Name = categoryVm.Name
            };

            _context.Categories.Add(category);
            _context.SaveChanges();
        }

        public void Update (UpdateCategoryVm categoryVm)
        {
            Category category = new Category()
            {
                CategoryId = categoryVm.CategoryId,
                Name = categoryVm.Name
            };

            _context.Categories.Update(category);
            _context.SaveChanges();
        }

        public void Delete (Category category)
        {
            _context.Categories.Remove(category);
            _context.SaveChanges();
        }
    }

Bạn cần lưu ý rằng repository trên được kế thừa từ interface repository, vì thế nếu thiếu hoặc hoặc không trùng khớp các phương thì sẽ gây ra lỗi.

Bước 4: Ta tiến hành tạo empty controller với tên CategoryController.cs với code sau:

    [ApiController]
    public class CategoryController : Controller
    {
        private readonly ICategoryRepository _repository;

        public CategoryController(ICategoryRepository repository)
        {
            _repository = repository;
        }

        [HttpGet("categories")]
        public IActionResult GetCategories()
        {
            IEnumerable<Category> categories = _repository.GetAll();
            return Ok(categories);
        }

        [HttpGet("category/{id}")]
        public IActionResult Getcategory(int id)
        {
            Category category = _repository.GetById(id);

            if (category == null)
            {
                return NotFound();
            }

            return Ok(category);
        }

        [HttpPost("category")]
        public IActionResult Post([FromBody] CreateCategoryVm categoryVm)
        {
            if(categoryVm == null)
            {
                return BadRequest();
            }

            _repository.Create(categoryVm);

            return Ok(categoryVm);
        }

        [HttpPut("caregory/{id}")]
        public IActionResult Put(int id, [FromBody] UpdateCategoryVm categoryVm)
        {
            if(categoryVm == null)
            {
                return BadRequest();
            }

            if(id != categoryVm.CategoryId)
            {
                return NotFound();
            }

            _repository.Update(categoryVm);
            return Ok(categoryVm);
        }

        [HttpDelete("caregory/{id}")]
        public IActionResult Delete(int id)
        {
            Category category = _repository.GetById(id);

            if(category == null)
            {
                return BadRequest();
            }

            _repository.Delete(category);

            return Ok(category);
        }

    }

Ở đây, ta thấy được mỗi method đều dùng các repository để gọi các action crud. Mỗi method trong controller, ta có thể gọi nhiều repository khác nhau.

Bước 5: Tiếp theo ta cần DI repository và interface repository trong thức ConfigureServices() của Startup.cs như sau:

services.AddScoped(typeof(ICategoryRepository), typeof(CategoryRepository));


Testing

Vậy là hầu như các bước đã xong, bây giờ ta cần chạy lên và kiểm tra API của chúng ta. Khi tạo project .Net Core API mới, thì đã được tích hợp sẵn swagger, và bây giờ chúng ta chỉ cần test các API ngay trên swagger là được nhé.


Lời kết

Khi bạn đã có data sẵn thì có thể bỏ qua các bước codefirst và viết các repository cho riêng bạn. 

Trong project mình đã cố tình tách các class, interface ra các folder khác nhau, để dễ dàng phân biệt và bạn có thể scale project bằng cách đặt các class và interface này trong các tầng của project.

Trong project chưa code repository cho entity product, và đây cũng là best practice trong việc tìm hiểu repository pattern.

Để tiếp tục tìm hiểu, mình hướng bạn đến việc tìm hiểu Generic Repository để phân biệt và thực hành kiểm tra ưu nhược điểm giữa Generic Repository Pattern và Repository Pattern.

Mong bài viết hữu ích đến bạn.

Hieu Ho.

Đăng nhận xét

Mới hơn Cũ hơn