Inyeccion de dependencias de .NET Core – #2 Scoped2Singleton

Continuando con la serie empezada con los deadlock en la inyeccion de dependencias de .NET Core en esta ocasión vamos a hablar de otra práctica erronea como asignar una dependencia de tipo Scope a un componente registrado como Singleton. Empezaremos como siempre ilustrándolo con un pequeño ejemplo, dónde usaremos las mismas clases que en la entrada anterior:

Estas clases las registraremos como sigue:

Como observas, tenemos un componente SomeClass registrado como Singleton pero que depende de DependencyClass que está a su vez registrada como Scoped. El problema de este tipo de registros es que si no pensamos en realidad lo que estamos diciendo podemos pasar por alto que esta dependencia Scoped en realidad se convertirá en Singleton y, por lo tanto, podremos tener muchos problemas si no lo tenemos en consideración. Vamos a comprobarlo agregando el siguiente código después del registro de nuestras dependencias:

En el código anterior estamos simulando la creación de diferentes scopes, recuerde que en una aplicación ASP.NET Core cada request sería un nuevo Scope. Si observamos el HashCode de cada elemento Dependency nos será fácil ver que siempre tenemos el mismo valor.

Como hemos dicho al principio, esto no es un bug sino una consideración que tenemos que tener y ser consicente de ella, toda dependencia scope de un singleton se convierte en singleton.

En .NET Core 1.1 tenemos un pequeño flag para indicarle a nuestro sistema de DI que nos avise sobre estos posibles errores. Con el uso de este flag en la situación anterior se lanzaría una excepcion al intentar resolver SomeClass. En el siguiente fragmento podemos ver el codigo completo con el uso de este flag.

 

Saludos

Unai Zorrilla

3 comentarios en “Inyeccion de dependencias de .NET Core – #2 Scoped2Singleton”

  1. Ya me he encontrado este problema antes, y es muy interesante.

    Veo una vertiente a considerar, y es cuando quieres que un singleton gestione instancias scoped y que se comporten como scoped. Me explico con un ejemplo: un servicio de infraestructura de la aplicación que lo registras como singleton pero necesita acceso a datos, por lo que depende de un repositorio, UoW, DAO… que registras como scoped. ¿Cómo implementas eso?

    Mi opción 1 – Utilizas en el singleton el motor de IoC para resolver la instancia de la dependencia cada vez que la usan (por ejemplo en el get de la propiedad o en un método ad-hoc). No puedes guardar las instancias en el propio singleton o caes en el mismo problema de tu post. La pega es que te vinculas/acoplas con el motor de IoC y se complican los tests unitarios: En este caso se trata de mockear el motor de IoC (para eso hay que aislarlo también) para devolver los mocks de las dependencias…

    Mi opción 2 – Implementas una infraestructura alternativa para gestionar este tipo de dependencias en los singleton (sobreingeniería en los singletons…) que sea fácilmente mockeable…

    ¿Cómo lo resolverías? ¿Te ha pasado alguna vez?

    1. Si el patrón es “constructor inyection” imposible, y en realidad es el patrón que mas me gusta :-). Si tienes elementos Scope porque quieres hacer Singleton el componente que tiene las dependencias?

      1. Imagínate un singleton que mantiene en memoria ciertos datos de la aplicación (configuración, diccionarios, …) que se precargan desde BD al arrancar y que se pueden invalidar en un momento dado en caliente, si alguien introduce algún cambio (ya se que se parece a una caché, pero hay más cosas por detrás que tienen que ver con modularidad, combinación de valores de distintos niveles…). El singleton es útil, y el acceso a datos debe ser scoped…

        Mientras escribía esto, estoy pensando que igual tienes razón y la respuesta es que no debería ser un singleton, sino que la combinación final debería almacenarla en cache o en un singleton sin dependencias…

        Bueno, me has hecho darle vueltas.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *