0Pricing
C# Academy · Lekcja

Wstrzykiwanie przez konstruktor i interfejsy

Stosuj wstrzykiwanie przez konstruktor oraz projektowanie oparte na interfejsach, aby rozdzielać komponenty i umożliwiać testy jednostkowe.

Wstrzykiwanie przez konstruktor i interfejsy to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.

Podstawy wstrzykiwania przez konstruktor

Wstrzykiwanie przez konstruktor jest najczęściej używanym wzorcem DI. Klasa deklaruje swoje zależności jako parametry konstruktora, a kontener dostarcza je podczas tworzenia instancji.

Dzięki temu zależności są jawne, wymagane i widoczne.

Definiowanie interfejsu

Należy zacząć od interfejsu definiującego kontrakt. Pozwala to wymieniać różne implementacje — rzeczywiste, mocki lub działające w pamięci — bez zmieniania kodu zależnego.

public interface IProductRepository
{
    Task<Product?> GetByIdAsync(int id);
    Task<IEnumerable<Product>> GetAllAsync();
    Task AddAsync(Product product);
}

Implementowanie interfejsu

Konkretna implementacja wykonuje właściwą pracę — wywołania bazy danych, żądania HTTP itp. Kod korzystający z usługi nie musi wiedzieć, którą implementację otrzymuje.

public class SqlProductRepository : IProductRepository
{
    private readonly AppDbContext _db;

    public SqlProductRepository(AppDbContext db) => _db = db;

    public async Task<Product?> GetByIdAsync(int id)
        => await _db.Products.FindAsync(id);

    public async Task<IEnumerable<Product>> GetAllAsync()
        => await _db.Products.ToListAsync();

    public async Task AddAsync(Product product)
    {
        _db.Products.Add(product);
        await _db.SaveChangesAsync();
    }
}

Korzystanie za pomocą konstruktora

Warstwa usług przyjmuje IProductRepository jako parametr konstruktora. Nie zna ona SqlProductRepository — zna jedynie kontrakt interfejsu.

public class ProductService
{
    private readonly IProductRepository _repo;
    private readonly ILogger<ProductService> _logger;

    public ProductService(
        IProductRepository repo,
        ILogger<ProductService> logger)
    {
        _repo = repo;
        _logger = logger;
    }

    public async Task<Product?> GetProductAsync(int id)
    {
        _logger.LogInformation("Fetching product {Id}", id);
        return await _repo.GetByIdAsync(id);
    }
}

Rejestrowanie całego łańcucha

Należy zarejestrować każdą usługę w łańcuchu. Kontener automatycznie rozwiąże cały graf zależności — ProductService → IProductRepository → AppDbContext.

builder.Services.AddDbContext<AppDbContext>(opt =>
    opt.UseSqlite("Data Source=app.db"));

builder.Services.AddScoped<IProductRepository, SqlProductRepository>();
builder.Services.AddScoped<ProductService>();

Testowanie za pomocą mocka

Ponieważ zależności są wstrzykiwane za pośrednictwem interfejsów, testy jednostkowe mogą przekazywać mock lub atrapę bez uzyskiwania dostępu do bazy danych. To jedna z największych korzyści DI.

// Using NSubstitute or Moq in a unit test
var mockRepo = Substitute.For<IProductRepository>();
mockRepo.GetByIdAsync(1).Returns(new Product { Id = 1, Name = "Widget" });

var logger = Substitute.For<ILogger<ProductService>>();
var svc = new ProductService(mockRepo, logger);

var result = await svc.GetProductAsync(1);
Assert.Equal("Widget", result?.Name);

Wiele parametrów konstruktora

Klasa może mieć wiele parametrów konstruktora. Kontener rozwiąże je wszystkie, o ile każdy z nich został zarejestrowany. Należy zachować zwięzłość konstruktorów — zbyt wiele parametrów sugeruje, że klasa ma zbyt wiele odpowiedzialności.

public class OrderService
{
    public OrderService(
        IOrderRepository orders,
        IProductRepository products,
        IEmailSender email,
        ILogger<OrderService> logger)
    {
        // all injected by DI container
    }
}

Unikanie antywzorca Service Locator

NIE należy wstrzykiwać IServiceProvider i wywoływać GetService wewnątrz metod biznesowych. Ukrywa to zależności i utrudnia testowanie. Należy preferować wstrzykiwanie przez konstruktor, aby kod był jawny i łatwy do testowania.

// BAD
public void Process(IServiceProvider sp)
{
    var repo = sp.GetService<IProductRepository>(); // hidden dep!
}

// GOOD
public class Processor
{
    private readonly IProductRepository _repo;
    public Processor(IProductRepository repo) => _repo = repo;
}

Składnia konstruktora podstawowego (C# 12)

C# 12 wprowadza konstruktory podstawowe dla wszystkich typów klas, umożliwiając deklarowanie parametrów bezpośrednio w deklaracji klasy, co pozwala zwięźlej zapisywać kod DI.

// C# 12 primary constructor
public class ProductService(
    IProductRepository repo,
    ILogger<ProductService> logger)
{
    public async Task<Product?> GetAsync(int id)
    {
        logger.LogInformation("Getting {Id}", id);
        return await repo.GetByIdAsync(id);
    }
}

Wymiana implementacji

Kluczową zaletą DI opartego na interfejsach jest możliwość wymiany implementacji w jednej linii bez zmieniania kodu, który z niej korzysta. Jest to przydatne przy zmianie baz danych, dostawców poczty lub flag funkcji.

// Switch from SQL to in-memory for integration tests
if (environment.IsEnvironment("Test"))
    builder.Services.AddScoped<IProductRepository, InMemoryProductRepository>();
else
    builder.Services.AddScoped<IProductRepository, SqlProductRepository>();

W praktyce: kontroler z DI

Kontrolery ASP.NET Core natywnie korzystają ze wstrzykiwania przez konstruktor. Platforma rozwiązuje wszystkie parametry i przekazuje je do kontrolera.

[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
    private readonly ProductService _svc;

    public ProductsController(ProductService svc) => _svc = svc;

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(int id)
    {
        var product = await _svc.GetProductAsync(id);
        return product is null ? NotFound() : Ok(product);
    }
}

Szybkie sprawdzenie

Jaka jest główna korzyść z programowania z użyciem interfejsów podczas korzystania z DI?

Podsumowanie: wstrzykiwanie przez konstruktor i interfejsy

Najważniejsze informacje:

  • Należy deklarować zależności jako parametry konstruktora — kontener je dostarczy
  • Należy definiować interfejsy dla kontraktów usług i wstrzykiwać interfejs, a nie typ konkretny
  • Interfejsy ułatwiają tworzenie mocków w testach jednostkowych oraz wymianę implementacji
  • Konstruktory podstawowe w C# 12 ograniczają ilość kodu powtarzalnego w klasach intensywnie korzystających z DI
  • Należy unikać antywzorca Service Locator — zależności powinny pozostać jawne

Często zadawane pytania

Czy lekcja „Wstrzykiwanie przez konstruktor i interfejsy” jest bezpłatna?

Tak — pełny tekst „Wstrzykiwanie przez konstruktor i interfejsy” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Wstrzykiwanie przez konstruktor i interfejsy”?

Stosuj wstrzykiwanie przez konstruktor oraz projektowanie oparte na interfejsach, aby rozdzielać komponenty i umożliwiać testy jednostkowe. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C# Academy?

Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Wstrzykiwanie przez konstruktor i interfejsy”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?

Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Podstawy kontenera DI
  2. Czas życia usług: Transient, Scoped, Singleton
  3. Wstrzykiwanie przez konstruktor i interfejsy
  4. Wzorce Factory i Options
← Powrót do C# Academy