Autofac:risoluzione dei parametri di runtime senza dover passare il container

Autofac:risoluzione dei parametri di runtime senza dover passare il container

Sì, far passare il container ovunque è un anti-pattern.

Puoi evitarlo usando una fabbrica come questa:

(nota:tutto il codice in questa risposta non è stato testato, lo sto scrivendo in un editor di testo su una macchina senza Visual Studio)

public interface IServiceHelperFactory
{
    IServiceHelper CreateServiceHelper(string serviceName);
}

public class ServiceHelperFactory : IServiceHelperFactory
{
    private IContainer container;

    public ServiceHelperFactory(IContainer container)
    {
        this.container = container;
    }

    public IServiceHelper CreateServiceHelper(string serviceName)
    {
        return container.Resolve<ServiceHelper>(new NamedParameter("serviceName", serviceName));
    }
}

All'avvio, registri il ServiceHelperFactory in Autofac, come tutto il resto:

builder.RegisterType<ServiceHelperFactory>().As<IServiceHelperFactory>();

Poi, quando hai bisogno di un ServiceHelper da qualche altra parte, puoi ottenere la fabbrica tramite l'iniezione del costruttore:

public class SomeClass : ISomeClass
{
    private IServiceHelperFactory factory;

    public SomeClass(IServiceHelperFactory factory)
    {
        this.factory = factory;
    }

    public void ThisMethodCreatesTheServiceHelper()
    {
        var helper = this.factory.CreateServiceHelper("some service name");
    }
}

Creando la fabbrica stessa tramite l'iniezione del costruttore con Autofac, ti assicuri che la fabbrica sia a conoscenza del container, senza dover passare il container da solo.

Lo ammetto, a prima vista questa soluzione non sembra molto diversa dal passare direttamente il contenitore. Ma il vantaggio è che la tua app è ancora disaccoppiata dal contenitore:l'unico posto in cui il contenitore è noto (tranne l'avvio) è all'interno della fabbrica.

MODIFICA:

OK, dimenticavo. Come ho detto sopra, lo sto scrivendo su una macchina senza Visual Studio, quindi non sono in grado di testare il mio codice di esempio.
Ora che ho letto il tuo commento, ricordo che ho avuto un problema simile quando ho usato Autofac e ho provato a registrare il contenitore stesso.

Il mio problema era che dovevo registrare il contenitore nel builder.
Ma per ottenere la registrazione dell'istanza del contenitore, dovevo chiamare builder.Build() ... che crea il contenitore, il che significa che non posso registrare cose nel builder in seguito.
Non ricordo il messaggio di errore che ho ricevuto, ma immagino che tu abbia lo stesso problema ora.

La soluzione che ho trovato è stata creare un secondo builder, registrare lì il contenitore, e quindi utilizzare il secondo builder per aggiornare l'unico contenitore .

Ecco il mio codice funzionante da uno dei miei progetti open source:

All'avvio, registro il contenitore::

var builder = new ContainerBuilder();

// register stuff here

var container = builder.Build();

// register the container
var builder2 = new ContainerBuilder();
builder2.RegisterInstance<IContainer>(container);
builder2.Update(container);

...che viene poi utilizzato da un WindowService per creare nuove finestre WPF:

public class WindowService : IWindowService
{
    private readonly IContainer container;

    public WindowService(IContainer container)
    {
        this.container = container;
    }

    public T GetWindow<T>() where T : MetroWindow
    {
        return (T)this.container.Resolve<T>();
    }
}

Ho seguito il percorso dell'approccio sopra e funziona bene, tuttavia ho trovato impossibile eseguire il test unitario perché il metodo "Resolve<>" in IContainer è un metodo di estensione. Inoltre, non è mai stato davvero "giusto" con tutti i discorsi sul non passare il tuo contenitore in giro.

Sono tornato al tavolo da disegno e ho trovato il modo "corretto" per creare un'istanza di oggetti utilizzando Autofac Delegate Factorieshttp://docs.autofac.org/en/latest/advanced/delegate-factories.html


La risoluzione dovrebbe verificarsi solo per gli oggetti di composizione radice. Chiamare risoluzione è quasi lo stesso di "rinfrescare" un oggetto, che è un test dell'olfatto. Ci sono momenti in cui la risoluzione è dinamica e può essere determinata solo al volo, ma la maggior parte le dipendenze sono deterministiche e possono essere registrate in anticipo. Come farlo con Autofac è la sfida. La risposta premiata di @Christian Specht è una buona risposta, ma presuppone che tutto sia determinato in fase di esecuzione.

Per definire una catena di dipendenze in fase di progettazione, vedere l'argomento SO Registrazione della catena di dipendenze secondarie di Autofac...