web-dev-qa-db-fra.com

Quand utiliser TryAddSingleton ou AddSingleton?

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?

14
Michael Freidgeim

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.

33
Steven