invalido IoC

4

Ho un sacco di servizi che vengono registrati come singleton dal mio contenitore IoC all'avvio, tutti questi servizi hanno un costruttore che accetta una stringa di connessione utilizzata dal provider di archiviazione sottostante.

Questo è andato bene dato che la stringa di connessione era statica e non è mai cambiata durante una sessione, tuttavia, ora ho bisogno di implementare la possibilità per gli utenti di cambiare la stringa di connessione in fase di esecuzione (ad esempio possono scegliere di puntare a un locale / cloud DB).

In definitiva, ciò significa che ho bisogno di in qualche modo invalidare tutti i miei servizi nel contenitore IoC e per quanto ho potuto vedere il mio contenitore IoC scelto non sembra fornire un mezzo per farlo , quindi tocca a me trovare una soluzione pratica.

Ora ci sono molti modi in cui posso farlo, ma sto cercando feedback, suggerimenti (se possibile dall'esperienza) su quella che sarebbe considerata la soluzione ottimale. Ecco un servizio semplificato

public class Service
{
    public Service (string connectionString)
    {
        this.ConnectionString = connectionString;
    }

    // marked as protected as derived services may need access to them
    protected string ConnectionString { get; set; }
    ...
}

Ed ecco come è attualmente registrato

public static void StartUp(IAppSettings appSettings)
{
    container.Register<IService, Service> (new Service(appSettings.ConnectionString));
    ...
}

La soluzione più semplice, che personalmente non mi piace, è semplicemente di avere tutti i miei servizi un riferimento a IAppSettings piuttosto che ConnectionString e utilizzare una proprietà di sola lettura, ad es.

protected string ConnectionString { get { return appSettings.ConnectionString; } }

Tuttavia, dato tutto ciò che i servizi necessitano da IAppSettings è la stringa di connessione, ciò sembra errato dal punto di vista della modellazione. Mantenere questo approccio, ma migliorarlo leggermente significherebbe introdurre una nuova interfaccia che esponga solo le informazioni necessarie da IAppSettings e.g.

public interface IDbSettings
{
    string ConnectionString { get; set; }
}

public class AppSettings : IAppSettings, IDbSettings
{
    ...
}
...
container.Register<IService, Service> (new Service(appSettings as IDbSettings));

Dico leggermente perché alla fine ho ancora lo stesso problema (i servizi fanno ancora riferimento a un oggetto di grandi dimensioni per una proprietà semplice).

Il tipo di approccio che vorrei e quello che ritengo sarebbe il migliore sarebbe estendere il contenitore IoC corrente per introdurre un metodo Invalidate in base al quale posso semplicemente impostare l'istanza interna su null e averla -inizializzato quando risolto. Vedo questo aspetto come

appSettings.ConnectionString = "database=db1";
container.Register<IService, Service>(new Service(appSettings.ConnectionString));
...
var svc = container.Resolve<IService>(); // connects to db1
appSettings.ConnectionString = "database=db2";
...
var svc = container.Resolve<IService>(); // still connects to db1
container.Invalidate<IService>();
var svc = container.Resolve<IService>(); // connects to db2

Per me questo offre benefici a tutto tondo

  • Controlli esattamente quando vuoi che le modifiche abbiano effetto
  • Si mantiene con lo schema Singleton
  • Significa che l'unica cosa che cambia è il contenitore IoC

Sarei interessato a tutti i pensieri, le critiche ecc.

    
posta James 06.02.2015 - 22:46
fonte

3 risposte

2

Il tuo secondo approccio è quello giusto, credo.

I say slightly because ultimately I still have the same problem (services are still referencing a large object for one simple property).

La dimensione dell'oggetto a cui gli altri oggetti si riferiscono non è il problema. La dimensione dell'interfaccia a cui si riferiscono è il problema. Non importa che l'interfaccia sia implementata da un oggetto che ha 1000 proprietà su di esso, se l'interfaccia espone solo una singola proprietà.

Penso che la tua terza soluzione sia una soluzione davvero pessima. Sta creando un'estrazione che perde molto. Perché la classe responsabile della modifica della stringa di connessione dovrebbe preoccuparsi delle altre classi che utilizzano la stringa di connessione? Perché il contenitore IOC dovrebbe preoccuparsi dello stato dei suoi oggetti e non semplicemente della gestione della loro vita?

Lo scopo principale di un contenitore IoC è gestire il grafico dell'oggetto. Gestisce la creazione e la distruzione dei tuoi oggetti per te. Ti permette di scrivere più facilmente codice che segue il principio di inversione delle dipendenze. In questo caso, la dipendenza è la stringa di connessione. Se molte delle tue classi dipendono solo dalla stringa di connessione, allora ha senso che esse debbano dipendere da un'interfaccia che definisce solo una stringa di connessione (e qualsiasi metodo / proprietà correlata in modo specifico a quella stringa di connessione). Non importa quale classe implementa effettivamente quell'interfaccia e fornisce quella funzionalità (a meno che quella classe abbia problemi di prestazioni significativi).

Questo fa parte del punto DIP. Permette di ridefinire le classi dietro le interfacce senza doversi preoccupare delle classi che usano le interfacce. In questo caso, se hai creato l'interfaccia per le stringhe di connessione e hai usato tutto, sarebbe un gioco da ragazzi per rifattorizzare la tua classe appSettings in una classe AppSettings e ConnectionSettings dietro le quinte senza influenzare nessuna delle classi dipendenti.

Seguire il principio di inversione delle dipendenze è il modo migliore per andare in questo caso. Crea la piccola interfaccia e fai dipendere il tuo codice. Il contenitore IoC può quindi fare ciò che è stato progettato per fare.

    
risposta data 08.04.2015 - 03:33
fonte
1

La costruzione dei tuoi servizi è costosa? Che ne dici di abbattere?

Se i tuoi oggetti sono singleton e sono responsabili della creazione e del mantenimento di una connessione al database per tutta la vita, come gestisci le transazioni o le richieste simultanee al database?

Penso che una soluzione migliore sarebbe quella di mettere la connettività del database in un IDBService di vita temporaneo e dare ai tuoi servizi una vita transitoria con una dipendenza da IDBService.

    
risposta data 06.02.2015 - 23:21
fonte
0

Non ho familiarità con TinyIoC, ma supponendo che tu stia memorizzando nella cache o puoi memorizzare nella cache i servizi registrati in qualche tipo di contenitore, come map/dictionary , potresti impostare key come hash da connectionString e il value come calcestruzzo Service .

Potresti avere un metodo Service getService(string hash) che restituirebbe il servizio basato sull'hash passato.

Service connection = IoCContrainer.getService(appSettings.ConnectionString.hashCode());
// use the connection
    
risposta data 05.09.2015 - 11:45
fonte

Leggi altre domande sui tag