Привязка Singleton Scope не работает должным образом
Я использую плагин ninject mvc3 с моим веб-приложением API. У меня есть привязка, которая выглядит так:
kernel.Bind<IFoo>().To<Foo>().InSingletonScope();
Я понимаю, что ядро создаст ровно один экземпляр Foo
и использовать его соответствующим образом. Помещая точку останова в Foo
конструктор, я ясно вижу, что он вызывается один раз за запрос, и я не могу объяснить, почему.
Мое единственное предположение, что каким-то образом новое ядро создается по запросу, но это не так, как CreateKernel
метод, который устанавливает средство разрешения глобальных зависимостей, запускается только один раз за время существования приложения.
Я использую некоторый код, взятый из этого поста, чтобы сделать ninject удобнее с mvc 4. Из-за изменений в фреймворке мне пришлось сделать дополнительную оболочку, которую я назначил GlobalConfiguration.Configuration.DependencyResolver
:
public class NinjectResolver : NinjectScope, IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectResolver(IKernel kernel)
: base(kernel)
{
_kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectScope(_kernel.BeginBlock());
}
}
Что я делаю неправильно?
4 ответа
Я никогда не мог заставить это работать должным образом, и я не уверен почему. Я предполагаю, что это как-то связано с интеграцией MVC4, которая в настоящее время немного незрелая.
В качестве альтернативы я использую:
kernel.Bind<IFoo>().ToConstant(new Foo());
Кажется, это работает, но я не слишком доволен этим.
Конечно, опоздал в этот поток, но это случилось со мной в Windows Service с хостингом OWIN для контроллеров Web API и разрешением зависимостей с помощью Ninject, InSingletonScope() не работал, пока я не сделал следующее:
var kernel = new StandardKernel();
...
kernel.Bind<Foo>().ToSelf().InSingletonScope();
kernel.Bind<IFoo>().ToMethod(context => context.Kernel.Get<Foo>());
...
// Controllers ask for the dependency as usual...
public class SomeController : ApiController
{
readonly IFoo _foo;
public SomeController(IFoo foo)
{
_foo = foo;
}
...
Надеюсь это поможет
Как упоминалось ранее, это выглядит как ошибка.
Один из вариантов - просто реализовать метод одноэлементного расширения:
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 static object padlock = new Object();
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)
{
lock (padlock)
{
if (_concreteInstance == null)
{
_concreteInstance = _kernel.Get<T>(k_ConstantInjectionName);
}
}
}
return _concreteInstance;
}).When(x => true);
return toReturn;
}
}
А затем просто используйте:
i_Kernel.SingletonBind<T>().To<TImplement>();
Скорее тогда
i_Kernel.Bind<T>().To<TImplement>().InSingletonScope();
Примечание: я использовал nuget для установки ninject & ninject.web.mvc (что, я уверен, вы также сделали).
Я не могу увидеть остальную часть вашего кода, но вот что у меня было в моем классе "NinjectDependencyScope". (Я думаю, что ваш просто называется NinjectScope, могут быть некоторые другие несоответствия именования с вашим кодом)
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot _resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
_resolver = resolver;
}
#region IDependencyScope Members
public void Dispose()
{
var 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);
}
#endregion
}
А вот мой класс NinjectWebCommon (находится в папке App_Start):
using System;
using System.Web;
using System.Web.Http;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Modules;
using Ninject.Web.Common;
[assembly: WebActivator.PreApplicationStartMethod(typeof(ABCD.Project.Web.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(ABCD.Project.Web.App_Start.NinjectWebCommon), "Stop")]
namespace ABCD.Project.Web.App_Start
{
public static class NinjectWebCommon
{
private static readonly Bootstrapper Bootstrap = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
Bootstrap.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
Bootstrap.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>();
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)
{
//var modules = new INinjectModule[] { new NinjectBindingModule(), };
//kernel.Load(modules);
Here's where you would load your modules or define your bindings manually...
}
}
}