asp.net-web-api - ninject not working with web api
Ninject InSingletonScope con Web Api RC (3)
Tengo algunas dificultades para utilizar el enlace InSingletonScope de Ninject con Web Api RC. No importa cómo creo mi enlace, parece que Web Api está manejando scope / lifetime en lugar de Ninject.
He intentado algunas variaciones en el cableado de Ninject. El más común es idéntico a la respuesta aquí: enlace de API Web ASP.NET con ninject
También probé esta versión: http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/
En ambos, estoy literalmente creando un proyecto Web Api listo para usar, luego agrego los paquetes de Ninject como se describe en cualquiera de las publicaciones. Finalmente, estoy agregando las clases Resolver y Scope, como esta para la versión de StackOverflow:
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
this.resolver = resolver;
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.GetAll(serviceType);
}
}
y:
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
Entonces, NinjectWebCommon se ve así:
using System.Web.Http;
using MvcApplication2.Controllers;
[assembly: WebActivator.PreApplicationStartMethod(typeof(MvcApplication2.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(MvcApplication2.App_Start.NinjectWebCommon), "Stop")]
namespace MvcApplication2.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// Register Dependencies
RegisterServices(kernel);
// Set Web API Resolver
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ILogger>().To<Logger>().InSingletonScope();
}
}
}
Los objetos ILogger y Logger no hacen nada, pero ilustran el problema. Logger hace Debug.Writeline para que pueda ver cuándo se creó una instancia. Y cada actualización de la página muestra que se actualiza por llamada, en lugar del singleton que esperaba. Aquí hay un controlador que usa el registrador:
public class ValuesController : ApiController
{
private readonly ILogger _logger;
public ValuesController(ILogger logger)
{
_logger = logger;
_logger.Log("Logger created at " + System.DateTime.Now.ToLongTimeString());
}
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post(string value)
{
}
// PUT api/values/5
public void Put(int id, string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
Cuando puse información de rastreo en la creación del kernel, parece mostrar que el kernel solo se crea una vez. Entonces ... ¿qué es lo que no estoy viendo? ¿Por qué el singleton no persiste?
utilizar
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel);
}
y no deseche el kernel en el NinjectDependencyScope
@Remo Gloor Cuando ejecuto su código en InMemoryHost de WebAPI y ejecuto pruebas de integración todo funciona bien y tengo singleton. Si ejecuto la solución WebAPI dentro del servidor web VS Cassini la primera ejecución es exitosa y cuando hago clic en actualizar recibo una excepción: Error al cargar el componente Ninja ICache No se ha registrado tal componente en el contenedor de componentes del kernel.
Si devuelvo el código anterior con BeginBlock , funciona en Cassini, pero IsSingleton ya no funciona en las pruebas de integración.
En lugar de no eliminar el kernel (que no llamará a la disposición interna) simplemente puede implementar su propio singleton:
public static class NinjectSingletonExtension
{
public static CustomSingletonKernelModel<T> SingletonBind<T>(this IKernel i_KernelInstance)
{
return new CustomSingletonKernelModel<T>(i_KernelInstance);
}
}
public class CustomSingletonKernelModel<T>
{
private const string k_ConstantInjectionName = "Implementation";
private readonly IKernel _kernel;
private T _concreteInstance;
public CustomSingletonKernelModel(IKernel i_KernelInstance)
{
this._kernel = i_KernelInstance;
}
public IBindingInNamedWithOrOnSyntax<T> To<TImplement>(TImplement i_Constant = null) where TImplement : class, T
{
_kernel.Bind<T>().To<TImplement>().Named(k_ConstantInjectionName);
var toReturn =
_kernel.Bind<T>().ToMethod(x =>
{
if (i_Constant != null)
{
return i_Constant;
}
if (_concreteInstance == null)
{
_concreteInstance = _kernel.Get<T>(k_ConstantInjectionName);
}
return _concreteInstance;
}).When(x => true);
return toReturn;
}
}
Y luego simplemente usa:
i_Kernel.SingletonBind<T>().To<TImplement>();
En lugar
i_Kernel.Bind<T>().To<TImplement>().InSingletonScope();
Nota: aunque solo importa para la primera solicitud, esta implementación no es segura para subprocesos.