Buscar..


Observaciones

Todo el punto de la inyección de dependencia (DI) es reducir el acoplamiento de código. Imagine cualquier tipo de interacción que implique la creación de algo como en el "Ejemplo de dependencia de código duro".

Una gran parte de escribir código es la capacidad de probarlo. Cada vez que creamos una nueva dependencia, hacemos que nuestro código sea difícil de probar porque no tenemos control sobre esa dependencia.

¿Cómo probaría el código que depende de DataTime.Now por ejemplo? Siempre cambia así que no tienes referencia. Esto es cuando se inyecta un parámetro estable como su punto de partida. Puede controlarlo, puede escribir pruebas basadas en varios valores y asegurarse de que siempre obtenga el resultado correcto.

Por lo tanto, una buena opción es pasar una interfaz o una clase abstracta como un parámetro en el constructor DI.

Una interfaz representa un contrato bien definido, siempre puede confiar en los métodos para estar allí y siempre puede confiar en las firmas del método.

Una vez que empieces a usar DI se abrirán otros aspectos. Por ejemplo, incluso si pasa una interfaz en algún momento, necesitará una implementación real para realizar cualquier trabajo. Aquí es donde aparecen otros conceptos. Podemos usar IOC (Inversión de control) para resolver nuestras dependencias. Esto significa que le indicamos a nuestro código que siempre use una implementación específica para cualquier contrato. Por supuesto que hay otras formas de hacer esto. Siempre podríamos crear una instancia de cada contrato con una implementación específica y, a partir de ese momento, nuestro código puede usar esa parte:

public ILogging Logging { get; set }

En algún momento lo inicializamos.

Logging = new FileLogging();

esto siempre funcionará siempre y cuando nuestra clase cumpla con el contrato esperado:

public class FileLogging : ILogging

a partir del momento de inicialización siempre utilizamos el objeto de registro. Esto hace la vida más fácil porque si alguna vez decidimos cambiar y usar un DatabaseLogging, por ejemplo, solo tenemos que cambiar el código en un lugar y aquí es exactamente donde inicializamos la clase de registro.

¿Es DI solo bueno para la prueba? No, DI también es importante al escribir código mantenible. Permite que la separación de inquietudes sea clara.

Cuando escriba cualquier código, piense ... es comprobable, puedo escribir una prueba, es cuando se inyecta un valor DateTime en lugar de usar DateTime. Ahora tiene sentido.

Configuraciones de Ninject

Después de la instalación de un contenedor IoC (Inversión de control), se necesitan algunos ajustes para que funcione. En este caso, usaré Ninject. En el archivo NinjectWebCommon, que se encuentra en la carpeta App_Start, sustituya el método CreateKernel con:

private static IKernel CreateKernel()
    {  
        // Create the kernel with the interface to concrete bindings           
        var kernel = RegisterServices();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
           
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

Y el método RegisterServices con:

 private static StandardKernel RegisterServices()
        {
            Container container = new Container();
            // encapsulate the interface to concrete bindings in another class or even in another layer 
            StandardKernel kernel = container.GetServices();
            return kernel;
        } 

Cree una nueva clase para el enlace que en este caso se llama Container:

public class Container
{
    public StandardKernel GetServices()
    {
        // It is good practice to create a derived class of NinjectModule to organize the binding by concerns. In this case one for the repository, one for service and one for app service bindings
        return new StandardKernel(new NinjectRepositoryModule(),
            new NinjectServiceModule(),
            new NinjectAppServiceModule());
    }
}

Finalmente, en cada clase de NinjectModule derivada modifique los enlaces que sobrecargan el método de carga como:

public class NinjectRepositoryModule: NinjectModule
{
    public override void Load()
    {
        // When we need a generic IRepositoryBase<> to bind to a generic RepositoryBase<>
        // The typeof keyword is used because the target method is generic
        Bind(typeof (IRepositoryBase<>)).To(typeof (RepositoryBase<>));

        // When we need a IUnitOfWorkbind to UnitOfWork concrete class that is a singleton
        Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
    }
}

Otro ejemplo de NinjectModule derivado:

public class NinjectServiceModule :NinjectModule
{
    public override void Load()
    {
        // When we need a IBenefitService to BenefitService concrete class           
        Bind<IBenefitService>().To<BenefitService>();

        // When we need a ICategoryService to CategoryService concrete class  
        Bind<ICategoryService>().To<CategoryService>();

        // When we need a IConditionService to ConditionService concrete class  
        Bind<IConditionService>().To<ConditionService>();           
    }
}

Utilización de las interfaces.

En la clase concreta que necesita el servicio, use la interfaz para acceder al servicio en lugar de su implementación como:

 public class BenefitAppService
{
    private readonly IBenefitService _service;
    public BenefitAppService(IBenefitService service)
    {
        _service = service;
    }      
   
    public void Update(Benefit benefit)
    {
        if (benefit == null) return           
        _service.Update(benefit);
        _service.Complete();
    }        
}

Ahora, si necesita algo en la clase concreta, no interferirá en el código anterior. Puede cambiar la implementación del servicio por otra diferencia completamente, y siempre que satisfaga la interfaz, estará listo. También hace que sea muy fácil probarlo.

Inyección de dependencia del constructor

La Inyección de dependencia del constructor requiere parámetros en el constructor para inyectar dependencias. Así que tienes que pasar los valores cuando creas un nuevo objeto.

public class Example
{
    private readonly ILogging _logging;

    public Example(ILogging logging)
    {
        this._logging = logging;
    }
}

Dependencia codificada

public class Example
{
    private FileLogging _logging;

    public Example()
    {
        this._logging = new FileLogging();
    }
}

parámetro DI

public DateTime SomeCalculation()
{
     return DateTime.Now.AddDays(3);
}

vs

public DateTime SomeCalculation(DateTime inputDate)
{
    return inputDate.AddDays(3);
}

Inyección De la Dependencia De Ninject

El resolvedor de dependencias se utiliza para evitar clases estrechamente acopladas, mejorar la flexibilidad y facilitar las pruebas. Puede crear su propio inyector de dependencia (no recomendado) o usar uno de los inyectores de dependencia bien escritos y probados. En este ejemplo voy a usar Ninject .

Paso uno: Crear resolución de dependencias.

En primer lugar, descargue Ninject desde NuGet. Cree una carpeta llamada Infraestructura y agregue una clase llamada NinjectDependencyResolver :

using Ninject;
using System;
using System.Collections.Generic;
using System.Web.Mvc;

public class NinjectDependencyResolver
    : IDependencyResolver
{
    private IKernel kernel;

    public NinjectDependencyResolver()
    {
        // Initialize kernel and add bindings
        kernel = new StandardKernel();
        AddBindings();
    }

    public object GetService(Type serviceType)
    {
        return kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return kernel.GetAll(serviceType);
    }

    private void AddBindings()
    {
        // Bindings added here
    }
}

El MVC Framework llamará a los métodos GetService y GetServices cuando necesite un aviso de una clase para atender una solicitud entrante.

Paso dos: Registrar resolución de dependencias.

Ahora tenemos nuestro sistema de resolución de dependencias personalizado y debemos registrarlo para que el marco MVC use nuestro sistema de resolución de dependencias. Registre el resolvedor de dependencias en el archivo Global.asax.cs :

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    DependencyResolver.SetResolver(new NinjectDependencyResolver());
    
    // .....
}

Paso tres: Añadir los enlaces.

Imagina que tenemos la siguiente interfaz e implementación:

public interface ICustomCache
{
    string Info { get; }
}

public class CustomCache : ICustomCache
{
    public string Info
    {
        get
        {
            return "Hello from CustomCache.";
        }
    }
}

Si queremos usar CustomCache en nuestro controlador sin acoplarlo estrechamente con CustomCache, entonces debemos vincular ICustomCache a CustomCache e inyectarlo usando Ninject. Lo primero es lo primero, vincule ICustomCache a CustomCache agregando el siguiente código al método AddBindings () de NinjectDependencyResolver :

private void AddBindings()
{
    // Bindings added here
    kernel.Bind<ICustomCache>().To<CustomCache>();
}

Luego prepare su controlador para inyección como se muestra a continuación:

public class HomeController : Controller
{
    private ICustomCache CustomCache { get; set; }

    public HomeController(ICustomCache customCacheParam)
    {
        if (customCacheParam == null)
            throw new ArgumentNullException(nameof(customCacheParam));

        CustomCache = customCacheParam;
    }

    public ActionResult Index()
    {
        // cacheInfo: "Hello from CustomCache."
        string cacheInfo = CustomCache.Info;

        return View();
    }
}

Este es un ejemplo de inyección costructora y es una forma de inyección de dependencia . Como puede ver, nuestro controlador de inicio no depende de la clase CustomCache itslef. Si queremos usar otra implementación de ICustomCache en nuestra aplicación, lo único que debemos cambiar es vincular a ICustomCache con otra implementación y ese es el único paso que debemos tomar. ¿Qué ha pasado aquí es, MVC marco pidió a nuestro solucionador de dependencias registrado para crear una instancia de la clase HomeController a través del método GetService. El método GetService le pide al kernel de Ninject que cree el objeto solicitado y el kernel de Ninject examina el tipo en su término y descubre que el constructor de HomeController vuelve a poner un ICustomCache y el enlace ya se ha agregado para el ICustomCache . Ninject crea una instancia de clase enlazada, la usa para crear HomeController y la devuelve MVC Framework.

Cadenas de dependencia.

Cuando Ninject intenta crear un tipo, examina otras dependencias entre el tipo y otros tipos y, si existe, Ninject intenta crearlos también. Por ejemplo, si nuestra clase CustomCache requiere ICacheKeyProvider y si se agrega la combinación de ICacheKeyProvider, Ninject puede proporcionarlo para nuestra clase.

Interfaz ICacheKeyProvider e implementación de SimpleCacheKeyProvider :

public interface ICacheKeyProvider
{
    string GenerateKey(Type type);
}

public class SimpleCacheKeyProvider
    : ICacheKeyProvider
{
    public string GenerateKey(Type type)
    {
        if (type == null)
            throw new ArgumentNullException(nameof(type));

        return string.Format("{0}CacheKey", type.Name);
    }
}

Clase modificada de CustomCache

public class CustomCache : ICustomCache
{
    private ICacheKeyProvider CacheKeyProvider { get; set; }

    public CustomCache(ICacheKeyProvider keyProviderParam)
    {
        if (keyProviderParam == null)
            throw new ArgumentNullException(nameof(keyProviderParam));

        CacheKeyProvider = keyProviderParam;
    }

    ...........
}

Añadir enlace para ICacheKeyProvider :

private void AddBindings()
{
    // Bindings added here
    kernel.Bind<ICustomCache>().To<CustomCache>();
    kernel.Bind<ICacheKeyProvider>().To<SimpleCacheKeyProvider>();
}

Ahora, cuando navegamos a HomeController, Ninject crea la instancia de SimpleCacheKeyProvider la usa para crear CustomCache y usa la instancia de CustomCache para crear HomeController .

Ninject tiene varias características excelentes, como la inyección de dependencia encadenada, y debes examinarlas si quieres usar Ninject.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow