0Pricing
C# Academy · Lezione

Iniezione tramite costruttore e interfacce

Applichi l'iniezione tramite costruttore con una progettazione basata sulle interfacce, così da disaccoppiare i componenti e abilitare i test unitari.

Iniezione tramite costruttore e interfacce è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Nozioni di base sull'iniezione tramite costruttore

L'iniezione tramite costruttore è il pattern DI più comune. Una classe dichiara le proprie dipendenze come parametri del costruttore e il container le fornisce al momento dell'istanziazione.

In questo modo le dipendenze sono esplicite, obbligatorie e visibili.

Definizione dell'interfaccia

Inizi con un'interfaccia che definisce il contratto. Questo consente di sostituire implementazioni diverse, reali, mock o in memoria, senza modificare il codice dipendente.

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

Implementazione dell'interfaccia

L'implementazione concreta gestisce il lavoro effettivo, come le chiamate al database e le richieste HTTP. Il consumer non deve mai sapere quale implementazione riceve.

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();
    }
}

Utilizzo tramite il costruttore

Il livello dei servizi riceve IProductRepository come parametro del costruttore. Non conosce mai SqlProductRepository: conosce solo il contratto dell'interfaccia.

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);
    }
}

Registrazione della catena

Registri ogni servizio della catena. Il container risolve automaticamente l'intero grafo delle dipendenze: ProductService → IProductRepository → AppDbContext.

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

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

Test con un mock

Poiché le dipendenze vengono inserite tramite interfacce, i test unitari possono utilizzare un mock o un fake senza accedere al database. Questo è uno dei principali vantaggi della 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);

Più parametri del costruttore

Una classe può avere numerosi parametri del costruttore. Il container li risolve tutti, purché ciascuno sia registrato. Mantenga i costruttori focalizzati: un numero eccessivo di parametri è un segnale che la classe ha troppe responsabilità.

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

Evitare l'anti-pattern Service Locator

NON inserisca IServiceProvider e chiami GetService all'interno dei metodi di business. Questo nasconde le dipendenze e rende difficili i test. Preferisca l'iniezione tramite costruttore per ottenere codice esplicito e verificabile.

// 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;
}

Sintassi del costruttore primario (C# 12)

C# 12 introduce i costruttori primari per tutti i tipi di classe, consentendo di dichiarare i parametri direttamente nella dichiarazione della classe e rendendo più conciso il codice 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);
    }
}

Sostituzione delle implementazioni

Un vantaggio fondamentale della DI basata sulle interfacce è la possibilità di sostituire un'implementazione con una sola riga, senza modificare il codice che la utilizza. È utile per cambiare database, provider di posta elettronica o feature flag.

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

Caso reale: controller con DI

I controller ASP.NET Core utilizzano nativamente l'iniezione tramite costruttore. Il framework risolve tutti i parametri e li passa al controller.

[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);
    }
}

Verifica rapida

Qual è il principale vantaggio di programmare verso le interfacce quando si utilizza la DI?

Riepilogo: iniezione tramite costruttore e interfacce

Punti chiave:

  • Dichiari le dipendenze come parametri del costruttore: sarà il container a fornirle
  • Definisca le interfacce per i contratti dei servizi; inserisca l'interfaccia, non il tipo concreto
  • Le interfacce facilitano l'utilizzo di mock nei test unitari e la sostituzione delle implementazioni
  • I costruttori primari di C# 12 riducono il codice ripetitivo nelle classi che usano molto la DI
  • Eviti l'anti-pattern Service Locator e mantenga esplicite le dipendenze

Domande Frequenti

La lezione «Iniezione tramite costruttore e interfacce» è gratuita?

Sì — il testo completo di «Iniezione tramite costruttore e interfacce» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Iniezione tramite costruttore e interfacce»?

Applichi l'iniezione tramite costruttore con una progettazione basata sulle interfacce, così da disaccoppiare i componenti e abilitare i test unitari. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Iniezione tramite costruttore e interfacce»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Fondamenti dei container DI
  2. Durata dei servizi: Transient, Scoped, Singleton
  3. Iniezione tramite costruttore e interfacce
  4. Pattern Factory e Options
← Torna a C# Academy