Семінар з програмного забезпечення
програмне забезпечення та веб => Asp (.net) => Тема розпочата: Okvaz від Квт. 12, 2025, 11:31 AM
Repository Pattern — це шаблон проєктування, який використовується в ASP[DOT]NET для відокремлення логіки доступу до даних від бізнес-логіки. Він діє як посередник між рівнем даних і бізнес-логікою, забезпечуючи централізований доступ до даних і полегшуючи тестування. У цій статті ми розглянемо, як працює Repository Pattern, надамо приклади коду та відповімо на поширені запитання у розділі FAQ.
Що таке Repository Pattern?Repository Pattern створює абстракцію між рівнем доступу до даних (наприклад, базою даних) і рештою програми. Це дозволяє:- Зменшити дублювання коду.
- Спростити тестування за допомогою мок-об'єктів.
- Зробити код більш читабельним і підтримувальним.
У контексті ASP[DOT]NET репозиторій зазвичай працює з Entity Framework Core для взаємодії з базою даних.
Структура Repository PatternТипова структура включає:- Інтерфейс репозиторію (IRepository): Визначає методи для роботи з даними.
- Конкретний репозиторій (Repository): Реалізує інтерфейс і взаємодіє з базою даних.
- Сервіс або контролер: Використовує репозиторій для виконання бізнес-логіки.
Приклади коду1. Налаштування моделі та контексту данихПрипустимо, ми створюємо додаток для керування списком книг. Спочатку визначимо модель Book і контекст даних.// 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. Створення інтерфейсу репозиторіюІнтерфейс визначає методи для роботи з книгами.// 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. Реалізація репозиторіюКонкретний клас репозиторію реалізує методи, визначені в інтерфейсі.// 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Щоб використовувати репозиторій, його потрібно зареєструвати в контейнері залежностей.// 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. Використання репозиторію в контролеріКонтролер використовує репозиторій для взаємодії з даними.// 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)Для уникнення дублювання коду можна створити універсальний репозиторій, який працює з будь-якою сутністю.// 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();
}
}
}
Реєстрація універсального репозиторію:builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
FAQ: Поширені запитання1. Чому варто використовувати Repository Pattern?Repository Pattern відокремлює логіку доступу до даних від бізнес-логіки, що полегшує тестування, заміну джерела даних (наприклад, з SQL на NoSQL) і підтримку коду.2. Чи потрібен репозиторій при використанні Entity Framework Core?Entity Framework Core вже надає абстракцію для роботи з даними, але репозиторій додає додатковий рівень контролю, спрощує тестування та уніфікує доступ до даних.3. Що краще: конкретний чи універсальний репозиторій?- Конкретний репозиторій ідеально підходить для специфічних операцій із певною сутністю.
- Універсальний репозиторій зменшує дублювання коду, але може бути менш гнучким для складних сценаріїв.
4. Як обробляти помилки в репозиторії?Додавайте обробку винятків у методи репозиторію або в контролері. Наприклад: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 для вирішення типових питань.