2014-12-04 6 views
6

consideri il seguente esempio:Come eseguire una registrazione in Simple Injector dopo una chiamata GetInstance/soluzione alternativa?

public class CommunicationClient : IClient 
{ 
    public CommunicationClient(IServerSettings settings) { ... } 
    // Code   
} 

public class SettingsManager : ISettingsManager 
{ 
    SettingsManager(IDbSettingManager manager) 

    // Code 
    public IDictionary<string, string> GetSettings() { ... } 
} 

Problema: Durante l'esecuzione di registrazioni (utilizzando SimpleInjector), ho bisogno di fornire i valori che si ottengono da un'istanza di SetingsManager e riempire ServerSettings esempio (tipo concreto per IServerSettings) ma se chiamo GetInstance<ISettingsManager> prima di registrare CommunicationClient, mi dà un errore che io non posso farlo
errore: Il il contenitore non può essere modificato dopo la prima chiamata a GetInstance, GetAllInstances e Verifica.)

Una soluzione potrebbe essere quella di iniettare ISettingsManager come una dipendenza per CommunicationClient ma davvero non voglio passare come avrebbe fornito più di informazioni necessarie ad esso.

EDIT: contenitore Registrazione

container.Register(typeof(ICommunicationClient), typeof(CommunicationClient)); 
ISettingsManager settingsManager = container.GetInstance<ISettingsManager>(); 

string url = settingsManager.GetSetting("url"); 
string userName = settingsManager.GetSetting("username"); 
string password = settingsManager.GetSetting("password"); 

container.Register(typeof(IServerConfiguration),() => 
     new ServerConfiguration(url, userName, password); 

Eventuali suggerimenti/soluzioni alternative su come realizzare al di sopra in un modo più pulito? Grazie.

+0

Ho modificato la domanda per includere la registrazione del contenitore. Grazie. – Ali

risposta

5

Iniettore semplice blocca il contenitore per ulteriori modifiche dopo il primo utilizzo. Questa è una scelta di design esplicita, che è descritta here. Ciò significa che non è possibile chiamare Register dopo aver chiamato GetInstance, ma non ci dovrebbe mai essere un motivo per farlo. In altre parole, la tua configurazione può sempre essere riscritta in un modo che non ti serve.Nel tuo caso la configurazione sarà probabilmente simile a questa:

var settingsManager = new SettingsManager(new SqlSettingManager("connStr")); 

container.RegisterSingle<ISettingsManager>(settingsManager); 
container.Register<ICommunicationClient, CommunicationClient>(); 

string url = settingsManager.GetSetting("url"); 
string userName = settingsManager.GetSetting("username"); 
string password = settingsManager.GetSetting("password"); 

container.Register<IServerConfiguration>(() => 
     new ServerConfiguration(url, userName, password)); 

Ci si vede che non è costruito SettingsManager-up dal contenitore. Quando si utilizza un contenitore DI, non è necessario consentire al contenitore DI di creare ogni istanza per l'utente. Lasciare per te le istanze di cablaggio automatico del contenitore è fatto per ridurre il carico di manutenzione del tuo Composition Root e semplifica l'applicazione dei problemi trasversali (ad esempio utilizzando decoratori) a gruppi di classi correlate. Nel caso delle classi SettingsManager e SqlSettingsManager, è molto improbabile che il loro costruttore cambi di tanto in tanto che aumenterà il carico di manutenzione della composizione della radice. È quindi perfettamente corretto creare manualmente tali istanze una sola volta.

+0

Grazie per aver lavorato me. – Ali

+0

@Steven Sto usando un gran numero di profili AutoMapper, sparsi per il codice, che sono registrati alla radice della composizione e possono dipendere da qualche servizio simile. Devo creare manualmente tutti i servizi richiesti? Devo essere in grado di cambiare facilmente il gestore delle impostazioni. – mrmashal

+0

@mrmashal si prega di inviare una nuova domanda qui su SO o su Github con qualche codice per descrivere il vostro caso d'uso. È difficile dare un feedback concreto senza tale contesto. – Steven

1

Se ho capito bene, per creare la classe CommunicationClient, è necessario passare le informazioni che vengono recuperate chiamando un metodo su un'istanza della ISettingsManager, ma non si vuole passare il ISettingsManager come una dipendenza alla vostra CommunicationClient ?

Una soluzione sarebbe quella di creare e registrare una factory che avrebbe una dipendenza da ISettingsManager e che avrebbe un metodo CreateClient che restituirebbe il client configurato.

public class CommunicationClientFactory : ICommunicationClientFactory 
{ 
    public CommunicationClientFactory(ISettingsManager settingsManager) {...} 

    public CreateClient() {...} 
} 

In questo modo il CommunicationClient non dipende dalla ISettingsManager e basta questa fabbrica che fa il lavoro di creazione dell'istanza.

Modifica: Un'alternativa, se non si desidera creare una fabbrica per questo, sarebbe avere l'oggetto CommunicationClient creato in uno stato "non valido" e disporre di un metodo che imposti le impostazioni e faccia il suo stato è valido.

Qualcosa di simile:

public class CommunicationClient : IClient 
{ 
    public CommunicationClient() { ... } 
    // Code 

    CommunicationClient WithSettings(IServerSettings settings) { ... } 
} 

Certo, poi ci si deve fare in modo che l'utente non utilizzare quando le impostazioni non sono state ancora passato, potenzialmente l'invio di un'eccezione se sarebbe il caso. Mi piace meno questa soluzione, perché è meno esplicito che tu abbia bisogno di quelle impostazioni per avere il tuo oggetto in uno stato corretto.

+0

Questo risolverebbe il problema, ma qui non è una fabbrica un eccessivo? Inoltre sto usando un contenitore IoC (SimpleInjector) per ottenere istanze e gestire la loro vita. – Ali

+0

E quindi dovrei passare il contenitore IoC a CommunicationClientFactory per creare l'istanza. – Ali

+0

Non penso che il passaggio del contenitore IoC alla fabbrica sia un grosso problema, dato che è solo una fabbrica, e potresti anche avere più logica al suo interno. Con SimpleInjector non penso ci sia un'alternativa, dato che non c'è modo di passare i valori in un costruttore quando si usa la factory anonima (registrando un 'Fun ') – Gimly

Problemi correlati