In che modo un contenitore di Dipendenza iniezione / IOC conosce quale implementazione utilizzare?

6

Quando usi un contenitore IoC, in questo modo:

var svc = IoC.Resolve<IShippingService>();

In che modo il contenitore IoC sceglie quale implementazione di IShippingService istanziare?

Inoltre, se sto chiamando quanto sopra per sostituire il codice equivalente:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

Suppongo che ProductLocator , PricingService , ecc. vengano richiamati nei parametri del costruttore come interfacce, non come classi concrete. In che modo il contenitore IoC sa quali implementazioni di IProductLocator , IPricingService , ecc. Per istanziare?

Il contenitore IoC è abbastanza intelligente da usare lo stesso ConfigProvider per entrambe le dipendenze (se questo è il requisito)?

    
posta Robert Harvey 21.03.2016 - 18:29
fonte

4 risposte

4

Perché si passa a configurarlo che gli dice come farlo. In genere ciò si verifica durante l'avvio dell'applicazione.

Nel caso più semplice potresti avere molte linee come:

iocConfig.Bind<IShippingService>().To<ShippingService>();

Ci sono diversi modi per definire una tale configurazione, come le convenzioni (ad esempio una classe concreta che corrisponde al nome dell'interfaccia oltre al prefisso I ), attributi o file di configurazione, ciascuno con i propri vantaggi e svantaggi.

Durante i test è possibile creare manualmente gli stub o utilizzare una configurazione diversa.

La maggior parte dei contenitori supporta anche un modo per il sito di destinazione di influenzare la risoluzione delle dipendenze, ad esempio aggiungendo un attributo. Personalmente non ne ho avuto bisogno fino ad ora.

Il riutilizzo delle istanze è determinato da ambito . In genere si ha un ambito in cui nulla viene riutilizzato (transitorio), ambito per richiesta, ambito per thread, ambito singleton, ecc. In genere si specifica l'ambito come parte della configurazione.

    
risposta data 21.03.2016 - 18:41
fonte
3

I contenitori richiedono l'installazione. Devi dirgli quello che vuoi quando chiedi un'implementazione di un'interfaccia.

Se c'è solo un'implementazione, il contenitore potrebbe essere in grado di risolverlo automaticamente, ma anche in questo caso, ci sono due casi:

1. L'implementazione dipende solo da classi concrete senza parametri di costruzione

Se questo è il caso, il contenitore di solito cerca le dipendenze, perché le conosce e le costruisce tutte, iniettandole.

2. L'implementazione dipende da interfacce o classi che richiedono tipi come stringhe, int, ecc.

In questo caso i parametri (siano essi i tipi scalari o le implementazioni concrete) devono essere nuovamente configurati per il caso specifico.

Se ci sono più implementazioni dell'interfaccia che stai chiedendo, devi dirgli cosa fare.

Se si dispone di un'interfaccia e si desidera scegliere un'implementazione durante il runtime, non penso che sia possibile risolverlo tramite un contenitore IoC e generalmente è consigliato un approccio di fabbrica.

    
risposta data 21.03.2016 - 18:41
fonte
2

Ho intenzione di rispondere a questo utilizzando uno specifico IoC, Castle Windsor per fondare questo in un esempio concreto. Diversi contenitori hanno funzionalità e convenzioni di denominazione leggermente diverse. Alcuni dettagli della tua domanda saranno specifici per l'implementazione.

Ci sono due modi fondamentali per registrare un componente uno per uno e per convenzione. Nella registrazione uno per uno elencherai un componente e l'interfaccia per cui desideri registrarlo. Puoi anche solo indicare il tipo e registrarlo alle interfacce implementate. Esempi

container.Register(
   Component.For<IMyService>().ImplementedBy<MyServiceImpl>()
);
container.Register(
    Component.For<MyServiceImpl>()
);

Tramite la registrazione della convenzione è possibile specificare alcune regole e registrare i tipi corrispondenti. È possibile registrare tipi che implementano un'interfaccia specifica o tutti i tipi che vivono in un assieme.

Per rispondere alla domanda implicita, di cosa succede quando si hanno più implementazioni di un'interfaccia. Il castello Windsor tornerebbe prima di tutto. Questo può essere un po 'opaco ma è almeno coerente.

Quando si registra un tipo viene assegnato uno stile di vita, che descrive quando il componente deve essere usato. Avete opzioni che vanno da una nuova ogni volta (transitoria) a singleton. Quindi se ConfigProvider è stato registrato come transitorio, sarebbero due istanze diverse, se singleton sarebbe un'istanza. Ci sono altri stili di vita disponibili, il più comune tra quelli disponibili per richiesta.

    
risposta data 21.03.2016 - 20:14
fonte
2

Penso che fornisca ulteriore colore a questo argomento menzionando i meccanismi di Autofac .

A differenza di CastleWindsor, come menzionato da @Sign, Autofac restituisce ciò che è registrato last . Citerò solo Documenti di Autofac come prova:

If more than one component exposes the same service, Autofac will use the last registered component as the default provider of that service:

builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>();

In this scenario, FileLogger will be the default for ILogger because it was the last one registered.

La documentazione di cui sopra continua dicendo che puoi ignorare quel comportamento attaccando un PreserveExistingDefaults() finale nella chiamata di registrazione.

Ma un altro caso d'uso interessante non menzionato in altre risposte qui è questo: che cosa succede se vuoi veramente tutte le istanze registrate?

Con Autofac questo è semplice - e non ha nulla a che fare con le registrazioni. È come lo si usa. Se hai un costruttore che richiede una percentuale diIEnumerable<> da risolvere, restituisce tutte le istanze registrate. Usando gli esempi di cui sopra si dovrebbe fare solo questo per ottenere sia ConsoleLogger che FileLogger :

public MyWorker(IEnumerable<ILogger> loggers) { ... }
    
risposta data 23.03.2016 - 00:07
fonte