Fermer

janvier 23, 2024

Décoder DI dans Optimizely CMS 12 / Blogs / Perficient

Décoder DI dans Optimizely CMS 12 / Blogs / Perficient


DI ou Dependency Injection est un sujet complexe, non seulement dans Optimizely CMS, mais en général dans les implémentations principales de .net. Certains aspects que nous obtenons, comme les différentes façons dont les dépendances peuvent être injectées et laquelle est meilleure que d’autres, tandis que d’autres nous laissent perplexes. Souvent, nous finissons par effectuer de nombreux dépannages, fouiller dans le code interne et trouver les bonnes alternatives à DI, pour que les choses fonctionnent.

J’ai récemment appris moi-même des choses intéressantes, en résolvant une étrange erreur DI. Et j’ai pensé que cela nécessitait un article de blog pour d’autres comme moi, qui les rencontrent presque tous les deux jours.

Alors, voici le décodage de DI dans Optimizely CMS 12.

Comprendre DI dans le contexte Optimizely CMS

Pour donner une petite introduction, DI est essentiellement la façon dont nous enregistrons nos implémentations de service (interface) dans le moteur de résolution de service principal .net. Il existe plusieurs façons d’y parvenir. Certaines personnes préfèrent le faire explicitement, comme enregistrer des services comme :

services.AddTransient<IService, Service>();

Certains préfèrent l’approche implicite comme :

[ServiceConfiguration(Lifecycle = ServiceInstanceScope.Transient, ServiceType = typeof (IService))]
public class Service : IService

Découvrez des détails plus détaillés sur Documentation optimisée.

Ordre des opérations

Dans les implémentations CMS, nous ajoutons des modules d’initialisation et de configuration pour initialiser/configurer certains services et logiques. Dans CMS 12 en particulier, nous ajoutons de nombreuses logiques d’initialisation et de configuration dans le fichier de démarrage, soit directement, soit en tant que méthodes d’extension. La plupart de nos DI se situent dans cette logique d’initialisation/configuration. Pas seulement notre code personnalisé, mais également le DI interne d’Optimizely.

Notre fichier de démarrage dispose de deux méthodes :

  1. ConfigureServices (services IServiceCollection)
  2. Configurer (application IApplicationBuilder)

L’ordre des opérations ressemble donc à ceci :

  1. Lorsque Startup est appelé pour la première fois, il recherche tous les modules d’initialisation/configuration dans le code et les exécute. Une grande partie de notre logique DI est exécutée à cette étape et nos services sont enregistrés dans cet objet ServiceCollection. Vous pouvez trouver plus de détails sur la logique d’initialisation et l’ordre dans lequel les modules sont exécutés ici.
  2. Il exécute ensuite la méthode ConfigureServices.
  3. Après cela, il exécute tous les enregistrements de service implicites internes d’Optimizely (DI implicite).
  4. Enfin, il exécute la méthode Configure qui exécute la logique au niveau de l’application comme le routage, les middlewares, notfoundHandlers, etc. Mais il est juste de dire qu’à cette étape, nous avons déjà tous nos services et ceux d’Optimizely enregistrés dans le moteur DI du site.

Notre problématique spécifique ?

Alors, qu’est-ce qui nous a aidé à comprendre cette commande ? Nous voulions exécuter du code personnalisé au démarrage qui devait accéder à une implémentation de service spécifique à partir de notre code. À première vue, cela semble simple. Si notre service est enregistré à l’avance, nous devrions pouvoir y accéder à partir du moteur DI et appeler notre code personnalisé dans ConfigureServices.

Attraper? – Notre service injectait en interne un certain nombre d’autres services internes ainsi que Optimizely via Constructor Injection. Pour ceux d’entre vous qui ne connaissent pas Constructor Injection, voici un article intéressant expliquant cela et plus encore autour de DI.

Alors, quel était le problème ?

Un des services injectés via le constructeur était IContentRepository. L’implémentation par défaut d’IContentRepository injectée en interne IContentProviderManager via son constructeur. IContentProviderManager a également une implémentation par défaut dans Optimizely appelée DefaultContentProviderManagerqui est implicitement enregistré dans DI :

Intérieurement

Maintenant, lorsque j’ai appelé ma logique personnalisée depuis la méthode ConfigureServices() au démarrage, j’ai eu une erreur d’exécution :

InvalidOperationException: No service for type 'EPiServer.Core.Internal.DefaultContentProviderManager' has been registered.

Pourquoi cette erreur ?

Si nous revisitons l’ordre des opérations ci-dessus, les enregistrements de service implicites d’Optimizely (qui incluent celui nécessaire pour DefaultContentProviderManager) se produisent à l’étape 3 et notre code l’attend à l’étape 2. Et nous aurions pu résoudre ce problème en enregistrant explicitement ce service avant notre coutume. code, mais malheureusement il s’agit d’une classe « interne », nous ne pouvons donc pas y accéder directement.

Quelle est la solution?

La solution était simple. Nous avons déplacé notre logique personnalisée vers l’étape 4, dans la méthode Configure, permettant ainsi de terminer tous les enregistrements DI explicites et implicites avant d’en demander.

J’en ai personnellement rencontré tellement au cours des deux dernières années, depuis que j’ai commencé à travailler sur CMS 12, que je pense que cela vaut la peine d’être partagé. Aussi pour référence, j’ai utilisé JetBrains dotPeek outil pour explorer les implémentations internes des interfaces et des classes Optimizely afin de comprendre cette configuration.

Pour ceux d’entre vous qui sont sur le chemin de la mise à niveau, passant de CMS 11 à CMS 12 et recherchant une solution pour toute la logique StructureMap DI, voici mon autre article de blog cela donne beaucoup de détails.

Bon codage (et décodage) !






Source link