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.