J'ai remarqué dans certains exemples de base .net qu'il y a des appels à TryAddSingleton
et dans certains AddSingleton
lors de l'enregistrement des services.
Le décompilateur montre que TryAdd (appelé par TryAddSingleton) ajoute le paramètre "descripteur" spécifié à la "collection" si le type de service n'a pas déjà été enregistré.
Cela signifie-t-il qu'il est toujours plus sûr d'utiliser TryAddSingleton, au cas où une autre méthode/bibliothèque aurait déjà enregistré la même classe?
Comme vous l'avez déjà remarqué, la différence entre TryAddSingleton
et AddSingleton
est que AddSingleton
ajoute toujours l'enregistrement à la collection, tandis que TryAddSingleton
ne le fait que lorsqu'il existe aucune inscription pour le type de service donné.
Lorsqu'il existe plusieurs enregistrements pour le même type de service, mais qu'une seule instance est demandée, .NET Core renvoie toujours la dernière. Cela signifie que le comportement de AddSingleton
consiste à remplacer les instances pour les résolutions non-collection. Par exemple:
services.AddSingleton<IX, A>();
services.AddSingleton<IX, B>(); // ‘replaces’ A
IX x = container.GetService<IX>(); // resolves B
Cependant, pour les résolutions de collection, AddSingleton
se comporte complètement différemment, car dans ce cas, AddSingleton
se comporte comme une collection "ajoutée" aux enregistrements déjà existants pour ce type de service. Par exemple:
services.AddSingleton<IX, A>();
services.AddSingleton<IX, B>();
IEnumerable<IX> xs = container.GetServices<IX>(); // resolves A and B
Avec TryAddSingleton
cependant, l'enregistrement ne sera pas ajouté lorsqu'il existe déjà des enregistrements pour le type de service donné. Cela signifie que, indépendamment du moment où un type de service est résolu comme une seule instance ou comme un ensemble d'instances, l'enregistrement ne sera pas ajouté lorsqu'il y aura au moins une inscription. Par exemple:
services.TryAddSingleton<IX, A>(); // adds A
services.TryAddSingleton<IX, B>(); // does not add B, because of A
IX x = container.GetService<IX>(); // resolves A
services.TryAddSingleton <IX, A>(); // adds A
services.TryAddSingleton <IX, B>(); // does not add B, because of A
IEnumerable<IX> xs = container.GetServices<IX>(); // resolves A
TryAddSingleton
est particulièrement utile pour le code de framework et de bibliothèque tierce qui souhaite enregistrer ses propres composants dans le conteneur. Il permet à un développeur d'applications de remplacer l'enregistrement par défaut du framework ou de la bibliothèque, même si le développeur d'applications a enregistré ce composant avant que le framework ou la méthode d'extension tierce AddXXX
ne soit appelée. Par exemple:
services.TryAddSingleton<IX, A>(); // adds A
services.AddThirdPartyLibrary (); // calls services.TryAddSingleton <IX, B>();
IX x = container.GetService<IX>(); // resolves A
Si la bibliothèque tierce avait appelé AddSingleton
au lieu de TryAddSingleton
, le A
du développeur de l'application sera toujours remplacé, ce qui peut être déroutant pour le développeur. En tant que développeur d'applications, vous savez généralement ce que vous avez enregistré, ce qui rend l'utilisation de TryAddSingleton
pas très utile pour un développeur d'applications.
Je dirais même que, du point de vue d'un développeur d'applications, le comportement de AddSingleton
peut être très délicat, car il remplace implicitement un enregistrement existant, sans aucun avertissement. D'après mon expérience, ce comportement peut provoquer des erreurs de configuration difficiles à repérer. Une conception plus sûre aurait consisté à disposer des méthodes AddSingleton
, AppendSingleton
et ReplaceSingleton
, où AddSingleton
lèverait une exception en cas d'enregistrement, et ReplaceSingleton
supprimerait en fait l'enregistrement existant.