En théorie, pour ne pas avoir à se soucier de disposer d'une instance IoC statique, il suffit de suivre la règle suivante Règle du Fight Club - c'est-à-dire ne pas parler du fight club - c'est-à-dire ne pas parler du conteneur IoC.
Cela signifie que vos composants doivent ignorer en grande partie le conteneur IoC. Il ne doit être utilisé qu'au niveau le plus élevé lors de l'enregistrement des composants. Si une classe doit résoudre quelque chose, il faut vraiment l'injecter comme une dépendance.
Le cas trivial est assez facile. Si PaymentService
dépend de IAccount
ce dernier doit être injecté par IoC :
interface IAccount {
Deposit(int amount);
}
interface CreditCardAccount : IAccount {
void Deposit(int amount) {/*implementation*/}
int CheckBalance() {/*implementation*/}
}
class PaymentService {
IAccount account;
public PaymentService (IAccount account) {
this.account = account;
}
public void ProcessPayment() {
account.Deposit(5);
}
}
//Registration looks something like this
container.RegisterType<IAccount, CreditCardAccount>();
container.RegisterType<PaymentService>();
Le cas pas si trivial est celui où vous voulez injecter des enregistrements multiples. Cela s'applique tout particulièrement lorsque vous effectuez une sorte de conversion sur la configuration et que vous créez un objet à partir d'un nom.
Pour notre exemple de paiement, disons que vous voulez énumérer tous les comptes et vérifier leur solde :
class PaymentService {
IEnumerable<IAccount> accounts;
public PaymentService (IEnumerable<IAccount> accounts) {
this.accounts = accounts;
}
public void ProcessPayment() {
foreach(var account in accounts) {
account.Chackbalance();
}
}
}
Unity a la possibilité d'enregistrer plusieurs mappages d'interfaces vers des classes (il faut penser qu'ils doivent avoir des noms différents). Cependant, il ne les injecte pas automatiquement dans les classes qui prennent des collections de ces interfaces enregistrées. Ainsi, l'exemple ci-dessus déclenchera une exception d'échec de résolution au moment de l'exécution.
Si vous ne vous souciez pas que ces objets vivent éternellement, vous pouvez enregistrer PaymentService
d'une manière plus statique :
container.RegisterType<PaymentService>(new InjectionConstructor(container.ResolveAll<IAccount>()));
Le code ci-dessus enregistrera PaymentService
et utilisera une collection de IAccount
qui est résolu au moment de l'enregistrement.
Ou bien vous pouvez passer une instance du conteneur lui-même en tant que dépendance et laisser PaymentService
effectuer la résolution des comptes. Cela ne suit pas tout à fait la règle du Fight Club, mais c'est légèrement moins odorant que le localisateur de services statiques.
class PaymentService {
IEnumerable<IAccount> accounts;
public PaymentService (IUnityContainer container) {
this.accounts = container.ResolveAll<IAccount>();
}
public void ProcessPayment() {
foreach(var account in accounts) {
account.Chackbalance();
}
}
}
//Registration is pretty clean in this case
container.RegisterType<IAccount, CreditCardAccount>();
container.RegisterType<PaymentService>();
container.RegisterInstance<IUnityContainer>(container);