ASP.NET-дə Repository Pattern: Приклади та Пояснення

Автор Okvaz, Квт. 12, 2025, 11:31 AM

« попередня теа - настпна тема »

Okvaz

Repository Pattern — це шаблон проєктування, який використовується в ASP[DOT]NET для відокремлення логіки доступу до даних від бізнес-логіки. Він діє як посередник між рівнем даних і бізнес-логікою, забезпечуючи централізований доступ до даних і полегшуючи тестування. У цій статті ми розглянемо, як працює Repository Pattern, надамо приклади коду та відповімо на поширені запитання у розділі FAQ.

Що таке Repository Pattern?
Repository Pattern створює абстракцію між рівнем доступу до даних (наприклад, базою даних) і рештою програми. Це дозволяє:

  • Зменшити дублювання коду.
  • Спростити тестування за допомогою мок-об'єктів.
  • Зробити код більш читабельним і підтримувальним.

У контексті ASP[DOT]NET репозиторій зазвичай працює з Entity Framework Core для взаємодії з базою даних.

Структура Repository Pattern
Типова структура включає:
  • Інтерфейс репозиторію (IRepository): Визначає методи для роботи з даними.
  • Конкретний репозиторій (Repository): Реалізує інтерфейс і взаємодіє з базою даних.
  • Сервіс або контролер: Використовує репозиторій для виконання бізнес-логіки.

Приклади коду
1. Налаштування моделі та контексту даних
Припустимо, ми створюємо додаток для керування списком книг. Спочатку визначимо модель Book і контекст даних.
Код Select
// Models/Book.cs
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public int Year { get; set; }
}

// Data/ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;

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

    public DbSet<Book> Books { get; set; }
}


2. Створення інтерфейсу репозиторію
Інтерфейс визначає методи для роботи з книгами.
Код Select
// Repositories/IBookRepository.cs
public interface IBookRepository
{
    Task<IEnumerable<Book>> GetAllAsync();
    Task<Book> GetByIdAsync(int id);
    Task AddAsync(Book book);
    Task UpdateAsync(Book book);
    Task DeleteAsync(int id);
}



3. Реалізація репозиторію
Конкретний клас репозиторію реалізує методи, визначені в інтерфейсі.
Код Select
// Repositories/BookRepository.cs
using Microsoft.EntityFrameworkCore;

public class BookRepository : IBookRepository
{
    private readonly ApplicationDbContext _context;

    public BookRepository(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<Book>> GetAllAsync()
    {
        return await _context.Books.ToListAsync();
    }

    public async Task<Book> GetByIdAsync(int id)
    {
        return await _context.Books.FindAsync(id);
    }

    public async Task AddAsync(Book book)
    {
        await _context.Books.AddAsync(book);
        await _context.SaveChangesAsync();
    }

    public async Task UpdateAsync(Book book)
    {
        _context.Books.Update(book);
        await _context.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var book = await _context.Books.FindAsync(id);
        if (book != null)
        {
            _context.Books.Remove(book);
            await _context.SaveChangesAsync();
        }
    }
}


4. Реєстрація репозиторію в ASP[DOT]NET Core
Щоб використовувати репозиторій, його потрібно зареєструвати в контейнері залежностей.
Код Select
// Program.cs
using Microsoft.EntityFrameworkCore;
using Repositories;

var builder = WebApplication.CreateBuilder(args);

// Додаємо контекст даних
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// Реєструємо репозиторій
builder.Services.AddScoped<IBookRepository, BookRepository>();

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();
app.MapControllers();

app.Run();


5. Використання репозиторію в контролері
Контролер використовує репозиторій для взаємодії з даними.
Код Select
// Controllers/BooksController.cs
using Microsoft.AspNetCore.Mvc;
using Repositories;

[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{
    private readonly IBookRepository _bookRepository;

    public BooksController(IBookRepository bookRepository)
    {
        _bookRepository = bookRepository;
    }

    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        var books = await _bookRepository.GetAllAsync();
        return Ok(books);
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetById(int id)
    {
        var book = await _bookRepository.GetByIdAsync(id);
        if (book == null)
            return NotFound();
        return Ok(book);
    }

    [HttpPost]
    public async Task<IActionResult> Create(Book book)
    {
        await _bookRepository.AddAsync(book);
        return CreatedAtAction(nameof(GetById), new { id = book.Id }, book);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> Update(int id, Book book)
    {
        if (id != book.Id)
            return BadRequest();
        await _bookRepository.UpdateAsync(book);
        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> Delete(int id)
    {
        await _bookRepository.DeleteAsync(id);
        return NoContent();
    }
}


6. Універсальний репозиторій (Generic Repository)
Для уникнення дублювання коду можна створити універсальний репозиторій, який працює з будь-якою сутністю.
Код Select
// Repositories/IGenericRepository.cs
public interface IGenericRepository<T> where T : class
{
    Task<IEnumerable<T>> GetAllAsync();
    Task<T> GetByIdAsync(int id);
    Task AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(int id);
}

// Repositories/GenericRepository.cs
using Microsoft.EntityFrameworkCore;

public class GenericRepository<T> : IGenericRepository<T> where T : class
{
    private readonly ApplicationDbContext _context;
    private readonly DbSet<T> _dbSet;

    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
        _dbSet = _context.Set<T>();
    }

    public async Task<IEnumerable<T>> GetAllAsync()
    {
        return await _dbSet.ToListAsync();
    }

    public async Task<T> GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }

    public async Task AddAsync(T entity)
    {
        await _dbSet.AddAsync(entity);
        await _context.SaveChangesAsync();
    }

    public async Task UpdateAsync(T entity)
    {
        _dbSet.Update(entity);
        await _context.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var entity = await _dbSet.FindAsync(id);
        if (entity != null)
        {
            _dbSet.Remove(entity);
            await _context.SaveChangesAsync();
        }
    }
}

Реєстрація універсального репозиторію:
Код Select
builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));

FAQ: Поширені запитання
1. Чому варто використовувати Repository Pattern?
Repository Pattern відокремлює логіку доступу до даних від бізнес-логіки, що полегшує тестування, заміну джерела даних (наприклад, з SQL на NoSQL) і підтримку коду.

2. Чи потрібен репозиторій при використанні Entity Framework Core?
Entity Framework Core вже надає абстракцію для роботи з даними, але репозиторій додає додатковий рівень контролю, спрощує тестування та уніфікує доступ до даних.

3. Що краще: конкретний чи універсальний репозиторій?
  • Конкретний репозиторій ідеально підходить для специфічних операцій із певною сутністю.
  • Універсальний репозиторій зменшує дублювання коду, але може бути менш гнучким для складних сценаріїв.

4. Як обробляти помилки в репозиторії?
Додавайте обробку винятків у методи репозиторію або в контролері. Наприклад:
Код Select
public async Task AddAsync(Book book)
{
    try
    {
        await _context.Books.AddAsync(book);
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateException ex)
    {
        throw new Exception("Помилка при збереженні книги", ex);
    }
}

5. Чи можна використовувати Repository Pattern без Entity Framework?
Так, репозиторій може працювати з будь-яким джерелом даних, наприклад, файлами, API або NoSQL базами.

6. Як тестувати репозиторії?
Використовуйте бібліотеки, такі як Moq, для створення мок-об'єктів інтерфейсу репозиторію, або In-Memory Database для Entity Framework Core.

Висновок
Repository Pattern в ASP[DOT]NET — це ефективний спосіб організувати доступ до даних, зробити код модульним і легким для тестування. Завдяки прикладам, наведеним вище, ви можете створити як специфічні, так і універсальні репозиторії для своїх проєктів. Експериментуйте з кодом, адаптуйте його до ваших потреб і використовуйте FAQ для вирішення типових питань.