Quantcast
Channel: Planeta Código
Viewing all articles
Browse latest Browse all 2718

Variable not found: Registro y obtención de múltiples implementaciones de servicios en ASP.NET Core, y un caso práctico

$
0
0
ASP.NET CoreComo sabemos, ASP.NET Core incluye un sistema de inyección de dependencias que, aunque es algo simple comparado con otras alternativas más veteranas, cubre la mayoría de necesidades habituales. Por ejemplo, un aspecto que no es muy conocido y que puede ser útil en muchas ocasiones es su capacidad para registrar y recuperar múltiples implementaciones de una misma interfaz.

En este post vamos a ver cómo conseguirlo, y un caso práctico de uso de esta técnica en un escenario muy frecuente.

Registro de múltiples implementaciones de la misma interfaz

Como se puede intuir, el registro de distintas implementaciones de la misma interfaz se realiza… pues eso, registrándolas ;) No hay ningún misterio aquí, simplemente añadimos el par <TService, TImplementation> en la clase Startup de ASP.NET Core, usando el lifetime apropiado (singleton, en el ejemplo de abajo):
publicclass Startup
{
publicvoidConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IImageFileProcessor, JpegFileProcessor>();
services.AddSingleton<IImageFileProcessor, HeavyFileProcessor>();
... // Add more IImageFileProcessor implementations
}
...
}

Obtención de múltiples implementaciones registradas

Aquí viene la parte que es menos intuitiva, aunque tampoco anda muy lejos de lo que podríamos esperar del sistema de inyección de dependencias de ASP.NET Core. Para reclamar al contenedor las instancias de tipos asociados a la interfaz TService, simplemente añadimos un parámetro de tipo IEnumerable<TService> en el constructor:
publicclass ImageController: Controller
{
privatereadonly IEnumerable<IImageFileProcessor> _imageProcessors;

publicImageController(IEnumerable<IImageFileProcessor> imageProcessors)
{
_imageProcessors = imageProcessors;
}
...
}
Esta colección de instancias de TService que ya tenemos en memoria podemos utilizarla para implementar las funcionalidades que necesitemos para nuestra aplicación.

Pero mejor, veámoslo todo sobre un caso práctico…

Un caso práctico: SOLIDificar código

Imaginad un código como el siguiente, donde se muestra un método Process() que lo único que hacemos es llamar a otros métodos que procesan objetos de tipo ImageFile bajo determinadas circunstancias:
publicclass ImageFileProcessor
{
public ImageFile Process(ImageFile imageFile)
{
if (imageFile.Type == "JPG")
imageFile = ProcessJpeg(imageFile);

if (imageFile.Type == "PNG")
imageFile = ProcessPng(imageFile);

if (imageFile.Size > 100_000)
imageFile = ProcessHeavyFile(imageFile);

if (imageFile.Width > 1024 || imageFile.Height > 768)
imageFile = ProcessBigFile(imageFile);

[...] // Call to another processors when needed

return f;
}

private ImageFile ProcessJpeg(ImageFile f) { ... }
private ImageFile ProcessPng(ImageFile f) { ... }
private ImageFile ProcessHeavyFile(ImageFile f) { ... }
private ImageFile ProcessBigFile(ImageFile f) { .. }
[...] // Other processors
}
Entre otros, este código atenta directamente contra Principio de Responsabilidad Única, dando lugar a una clase que a todas luces tendrá a ser muy extensa y difícilmente mantenible al tener demasiadas responsabilidades. Nada que no podamos romper usando algo de Estrategia ;)

El primer paso para mejorar esta implementación sería detectar que en nuestra aplicación existen procesadores de archivos de imagen que se aplican en función de determinados criterios. Con esta información podríamos crear la siguiente abstracción:
publicinterface IImageFileProcessor
{
bool CanProcess(ImageFile imageFile);
ImageFile Process(ImageFile imageFile);
}
En nuestra aplicación existirán tantas implementaciones de IImageProcessor como formas de procesar los ficheros de imagen, y cada uno de estos componentes incluirá tanto la lógica para decidir si puede procesarlas como el proceso en sí mismo:
publicclass JpegFileProcessor : IImageFileProcessor
{
publicboolCanProcess(ImageFile imageFile) => imageFile.Type == "JPG";

public ImageFile Process(ImageFile imageFile)
{
// Process here the JPEG file
}
}

publicclass HeavyFileProcessor : IImageFileProcessor
{
publicboolCanProcess(ImageFile imageFile) => imageFile.Size > 100_000;

public ImageFile Process(ImageFile imageFile)
{
// Process here the heavy file
}
}

...
De momento, esta estructura ya nos permitiría simplificar la clase inicial, liberándola de responsabilidades que no le corresponden. Serán los componentes procesadores, las distintas implementaciones de IImageFileProcessor, los que llevarán ese peso.

Aplicando la técnica que hemos visto anteriormente, podríamos simplificar la clase inicial ImageFileProcessor suministrándole mediante inyección de dependencias la colección de procesadores, y haciendo que su método Process() simplemente los recorra y ejecute de forma secuencial:
publicclass ImageFileProcessor
{
privatereadonly IEnumerable<IImageFileProcessor> _imageProcessors;

publicImageFileProcessor(IEnumerable<IImageFileProcessor> imageProcessors)
{
_imageProcessors = imageProcessors;
}

public ImageFile Process(ImageFile imageFile)
{
foreach (var processor in _imageProcessors)
{
if (processor.CanProcess(imageFile))
{
imageFile = processor.Process(imageFile);
}
}
return imageFile;
}
}
Para que el componente ImageFileProcessor esté disponible en todos los puntos de nuestra aplicación deberíamos registrarlo en el contenedor de dependencias. Asimismo, ahí registraríamos todos los procesadores de imágenes que hayamos implementado:
publicclass Startup
{
publicvoidConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<ImageFileProcessor>();
services.AddSingleton<IImageFileProcessor, JpegFileProcessor>();
services.AddSingleton<IImageFileProcessor, HeavyFileProcessor>();
... // Add more IImageFileProcessor implementations
}
...
}
Ojo, aunque en este caso utilizamos un registro de tipo singleton, esto puede variar en cada escenario en función de las necesidades.
Fijaos que si quisiéramos añadir un nuevo procesador de imágenes, por ejemplo un GifProcessor no sería necesario tocar la clase ImageFileProcessor, ni ninguno de los procesadores existentes. Simplemente crearíamos la clase correspondiente implementando IImageFileProcessor y la registraríamos en el contenedor… and that’s all, folks!
...
services.AddSingleton<IImageFileProcessor, GifFileProcessor>();
En resumen, en este post hemos visto cómo utilizar el sistema de inyección de dependencias de ASP.NET Core para registrar múltiples implementaciones de la misma interfaz y cómo recuperarlas más adelante para implementar soluciones muy limpias a problemas frecuentes.

Espero que podáis aplicarlo a vuestros propios desarrollos ;)

Publicado en Variable not found.

Viewing all articles
Browse latest Browse all 2718

Trending Articles


Girasoles para colorear


mayabang Quotes, Torpe Quotes, tanga Quotes


Tagalog Quotes About Crush – Tagalog Love Quotes


OFW quotes : Pinoy Tagalog Quotes


Long Distance Relationship Tagalog Love Quotes


Tagalog Quotes To Move on and More Love Love Love Quotes


5 Tagalog Relationship Rules


Best Crush Tagalog Quotes And Sayings 2017


Re:Mutton Pies (lleechef)


FORECLOSURE OF REAL ESTATE MORTGAGE


Sapos para colorear


tagalog love Quotes – Tiwala Quotes


Break up Quotes Tagalog Love Quote – Broken Hearted Quotes Tagalog


Patama Quotes : Tagalog Inspirational Quotes


Pamatay na Banat and Mga Patama Love Quotes


Tagalog Long Distance Relationship Love Quotes


BARKADA TAGALOG QUOTES


“BAHAY KUBO HUGOT”


Vimeo 10.7.0 by Vimeo.com, Inc.


Vimeo 10.7.1 by Vimeo.com, Inc.