MidПрактика2 min

Шаг 14: Реализация Repository

OrderRepository на EF Core: Include для загрузки связанных данных, регистрация Infrastructure Layer через DependencyInjection

Infrastructure реализует интерфейсы из Domain через Entity Framework Core.

OrderRepository.cs

В папке Repositories:

using Microsoft.EntityFrameworkCore;
using OrderManagement.Domain.Entities;
using OrderManagement.Domain.Interfaces;
using OrderManagement.Infrastructure.Persistence;

namespace OrderManagement.Infrastructure.Repositories;

public class OrderRepository : IOrderRepository
{
    private readonly OrderDbContext _context;

    public OrderRepository(OrderDbContext context) =>
        _context = context;

    public async Task<Order?> GetByIdAsync(
        Guid id, CancellationToken ct = default)
    {
        return await _context.Orders
            .Include(o => o.Items)
            .FirstOrDefaultAsync(o => o.Id == id, ct);
    }

    public async Task<IReadOnlyList<Order>> GetByCustomerIdAsync(
        string customerId, CancellationToken ct = default)
    {
        return await _context.Orders
            .Include(o => o.Items)
            .Where(o => o.CustomerId == customerId)
            .OrderByDescending(o => o.CreatedAt)
            .ToListAsync(ct);
    }

    public async Task AddAsync(
        Order order, CancellationToken ct = default) =>
        await _context.Orders.AddAsync(order, ct);

    public void Update(Order order) =>
        _context.Orders.Update(order);

    public void Remove(Order order) =>
        _context.Orders.Remove(order);
}

.Include(o => o.Items) -- eager loading, загружает позиции заказа через SQL JOIN. Без Include свойство Items будет пустым. AddAsync помечает объект для INSERT, Update -- для UPDATE. Реальный SQL выполняется при SaveChangesAsync.

DependencyInjection.cs для Infrastructure

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using OrderManagement.Domain.Interfaces;
using OrderManagement.Infrastructure.Persistence;
using OrderManagement.Infrastructure.Repositories;

namespace OrderManagement.Infrastructure;

public static class DependencyInjection
{
    public static IServiceCollection AddInfrastructure(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        services.AddDbContext<OrderDbContext>(options =>
        {
            options.UseSqlServer(
                configuration.GetConnectionString("OrderDB"),
                sql => sql.EnableRetryOnFailure(
                    5, TimeSpan.FromSeconds(30), null));
        });

        services.AddScoped<IUnitOfWork>(sp =>
            sp.GetRequiredService<OrderDbContext>());
        services.AddScoped<IOrderRepository, OrderRepository>();

        return services;
    }
}

AddScoped -- один экземпляр на HTTP-запрос. sp.GetRequiredService<OrderDbContext>() гарантирует, что IUnitOfWork и DbContext -- один объект.

Проверь себя

Зачем Include(o => o.Items) при загрузке заказа?

Что делает AddScoped в DI-контейнере?

Почему IUnitOfWork регистрируется через GetRequiredService?