mvc c# ninject

c# - mvc - Enlace contextual por defecto de Ninject



ninject mvc 5 (3)

Es bastante posible hacer esto en Ninject, simplemente no es la forma en que la resolución se comporta de forma predeterminada. La IKernel.Get<T> no solicita el enlace "predeterminado", solicita ningún enlace; en otras palabras, no aplica ninguna restricción. Si hay más de un enlace coincidente, se lanza una excepción a ese efecto.

Pruebe estos dos métodos de extensión:

static class KernelExtensions { public static T GetDefault<T>(this IKernel kernel) { return kernel.Get<T>(m => m.Name == null); } public static T GetNamedOrDefault<T>(this IKernel kernel, string name) { T namedResult = kernel.TryGet<T>(name); if (namedResult != null) return namedResult; return kernel.GetDefault<T>(); } }

El primero obtiene el enlace "predeterminado", es decir, el que hayas enlazado que no tenga nombre . El segundo intenta obtener un enlace con nombre, pero si no lo encuentra, vuelve al valor predeterminado.

Por supuesto, Remo tampoco se equivoca; debe evitar usar Ninject o cualquier otro contenedor de esta manera, a menos que tenga una razón particularmente buena para hacerlo. Este es el patrón del localizador de servicio (anti), no la inyección de dependencia verdadera. Debería usar la sintaxis When para los enlaces condicionales, ya sea utilizando condiciones complejas o simplemente decorando las clases que necesitan enlaces especiales, es decir:

Bind<IFoo>().To<SpecialFoo>().WhenInjectedInto<ClassThatNeedsSpecialFoo>();

o...

Bind<IFoo>().To<SpecialFoo>().WhenMemberHas<SpecialAttribute>(); class InjectedClass { public InjectedClass([Special]IFoo) { ... } }

Esa es la forma correcta de manejar los enlaces predeterminados y condicionales. Los enlaces con nombre realmente solo son útiles cuando intentas implementar un patrón de fábrica y quieres envolver el contenedor IoC en tu fábrica personalizada. Está bien, pero úselo con moderación, ya que terminará desechando muchos / la mayoría de los beneficios de la inyección de dependencia de esa manera.

De forma alternativa, podría implementar su propio comportamiento de activación y usarlo para anular el valor predeterminado en Ninject: todo es modular y se incluye en la colección de "Componentes". Pero esto no es para un corazón débil, así que no planeo incluir un tutorial detallado aquí.

Tengo una interfaz con algunas implementaciones concretas diferentes. Estoy tratando de darle a Ninject un valor predeterminado para usar y solo usar la otra implementación si un nombre coincide. Por ejemplo, tengo los siguientes enlaces.

Bind<ISomething>().To<DefaultSomething>() Bind<ISomething>().To<OtherSomething>().Named("55abd8b8-097f-4e1c-8d32-95cc97910604");

Lo que me gustaría es que si la sección con nombre no coincide, usar la implementación DefaultSomething. Cuando paso en el guid explícitamente enlazado, funciona bien. Cuando paso cualquier otra guía, obtengo la excepción "No hay enlaces disponibles".

Bind<ISomething>().To<OtherSomething>().Named("55abd8b8-097f-4e1c-8d32-95cc97910604"); Bind<ISomething>().To<DefaultSomething>() Bind<ISomething>().To<DefaultSomething>() Bind<ISomething>().To<OtherSomething>().When(ctx => ctx.Service != null && ctx.Service.Name == "55abd8b8-097f-4e1c-8d32-95cc97910604");

También he intentado usar. Cuando se comprueba el enlace y he intentado revertir el orden como se muestra a continuación, sin embargo, nunca puedo enlazar a menos que pase el Guid que tiene el nombre explícito.

Este artículo parece indicar que los enlaces predeterminados funcionan, así que debo estar haciendo algo mal. ¿Alguna sugerencia?

Edición: Aquí hay un ejemplo completo que muestra el problema que estoy tratando de resolver. El comportamiento deseado es para kernel.Get<INumber>("Three").Write() para devolver "Unknown Number"

using System; using System.Collections.Generic; using System.Linq; using System.Text; using Ninject; namespace NinjectTest { interface INumber { string Write(); } class UnknownNumber : INumber { public string Write() { return "Unknown Number"; } } class One : INumber { public string Write() { return "1 = One"; } } class Two : INumber { public string Write() { return "2 = Two"; } } class Program { static void Main(string[] args) { StandardKernel kernel = new StandardKernel(); kernel.Bind<INumber>().To<UnknownNumber>(); kernel.Bind<INumber>().To<One>().Named("One"); kernel.Bind<INumber>().To<Two>().Named("Two"); Console.WriteLine(kernel.Get<INumber>("One").Write()); Console.WriteLine(kernel.Get<INumber>("Two").Write()); Console.WriteLine(kernel.Get<INumber>("Three").Write()); Console.ReadLine(); } } }


Has entendido completamente los enlaces nombrados:

Darle a un enlace un nombre NO es una condición. Aún los obtendrá todos cuando los solicite sin restricciones. Agregar un nombre no cambia absolutamente nada por sí mismo.

Solicitar una instancia usando un nombre agrega la restricción:

Sólo se devolverán los enlaces cuyo nombre coincida con el dado.

En su caso, me dio una instancia cuyo nombre de enlace es "three" . Y esperas que devuelva UnknownNumber , que ni siquiera tiene un nombre.

Esto se puede lograr ya sea por

  1. pasar un parámetro y agregar condiciones a los enlaces que verifican si el parámetro coincide, o
  2. pasando una restricción que se ajusta al nombre o la instancia sin nombre y declara la sin nombre como implícita.

Opción 1:

public class CustomerIdParameter : Parameter { public CustomerIdParameter(string id) : base("CustomerId", (object)null, false) { this.Id = id; } public string Id { get; private set; } } kernel.Bind<ISomething>().To<Default>(); kernel.Bind<ISomething>().To<Other>() .When(r => r.Parameters.OfType<CustomerIdParameter>() .Single().Id == "SomeName"); kernel.Get<IWeapon>(new CustomerIdParameter("SomeName")).ShouldBeInstanceOf<Sword>();

Lo dejo a usted para que escriba los métodos de extensión para que la definición y la resolución sean más fáciles.

Opcion 2:

Bind<ISomething>().To<Default>().Binding.IsImplicit = true; Bind<ISomething>().To<Other>().Named("SomeName") public static T GetNamedOrDefault<T>(this IKernel kernel, string name) { return kernel.Get<T>(m => m.Name == null || m.Name == name); }

Pero honestamente, creo que lo que quieres hacer no parece ser un diseño adecuado:

  1. Mantenga su acceso al núcleo al mínimo absoluto. Lo que estás haciendo aquí es un uso de Ninject similar a ServiceLocator.
  2. Si no hay un enlace disponible para una instancia esperada, prefiero esperar una excepción a usar una instancia predeterminada porque se trata de un error.

También puede simplemente agregar una condición para que su enlace no tenga una condición, como por ejemplo:

kernel.Bind<IObject>().To<Object1>().When( x => x.ParentContext != null && !x.ParentContext.Binding.IsConditional) .InRequestScope(); kernel.Bind<IObject>().To<Object2>().InRequestScope() .Named("WCFSession");

Al realizar una inyección estándar sin un nombre especificado, se utilizará el primer enlace. Al especificar un nombre, se utilizará el enlace nombrado. No es la solución más bonita, pero funciona.