SignalR 2 Инъекция зависимостей с помощью Ninject

У меня есть существующее приложение MVC, которое использует Injection Dependency с Ninject. Я установил пакет Ninject.MVC3 nuget и создал класс под названием NinjectWebCommon в моем App_Start, который полностью изолирует ядро ​​и регистрирует все мои привязки:

public static void Start()
{
    DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
    DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
    bootstrapper.Initialize(CreateKernel);
}

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
    kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
    RegisterServices(kernel);
    return kernel;
}

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IFoo>().To<Foo>();
}

У нас есть новое требование, которое мы думали, что SignalR сможет удовлетворить, поэтому мы установили пакет SignalR 2 nuget в проект. Я создал концентратор и немного поработал над тем, как внедрить инъекцию зависимостей в проект, и нашел статью, которая предлагает создать SignalRDependencyResolver. http://www.asp.net/signalr/overview/signalr-20/extensibility/dependency-injection

В статье вы создаете ядро ​​в файле Startup.cs, который используется для регистрации SignalR в OWIN:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {

        var kernel = new StandardKernel();
        var resolver = new NinjectSignalRDependencyResolver(kernel);

        kernel.Bind<IStockTicker>()
            .To<Microsoft.AspNet.SignalR.StockTicker.StockTicker>()  // Bind to StockTicker.
            .InSingletonScope();  // Make it a singleton object.

        kernel.Bind<IHubConnectionContext>().ToMethod(context =>
            resolver.Resolve<IConnectionManager>().GetHubContext<StockTickerHub>().Clients
            ).WhenInjectedInto<IStockTicker>();

        var config = new HubConfiguration()
        {
            Resolver = resolver
        };

        app.MapSignalR(config);

    }
}

Проблема заключается в том, что этот подход позволяет мне создавать два разных ядра, и они, похоже, имеют свой собственный набор зависимостей, которые они знают, как их разрешать. Если у меня есть зависимость, определенная в NinjectWebCommon, концентратор не знает, как разрешить эту зависимость. Не подвергая мое ядро ​​в NinjectWebCommon, каков правильный способ добавить DI в SignalR с помощью пакета Ninject.MVC3?

Ответ 1

Ни один из текущих ответов напрямую не отвечает на ваш вопрос. Кроме того, достижение результата, которого вы добиваетесь, очень просто, как только вы точно знаете, что делать. "Правильный" способ сделать это - установить преобразователь зависимостей SignalR в методе CreateKernel класса NinjectWebCommon.

Предполагая, что вы создали класс NinjectSignalRDependencyResolver, как вы упомянули, ни один другой код не нужно добавлять нигде, кроме строки, выделенной в фрагменте кода ниже:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
    kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

    // THIS LINE DOES IT!!! Set our Ninject-based SignalRDependencyResolver as the SignalR resolver
    GlobalHost.DependencyResolver = new NinjectSignalRDependencyResolver(kernel);

    RegisterServices(kernel);
    return kernel;
}

Помимо вышеизложенного, больше ничего не нужно делать, кроме объявления ваших привязок в методе RegisterServices NinjectWebCommon. В вашем примере это будет выглядеть так:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IStockTicker>()
        .To<Microsoft.AspNet.SignalR.StockTicker.StockTicker>()  // Bind to StockTicker.
        .InSingletonScope();  // Make it a singleton object.

    kernel.Bind<IHubConnectionContext>().ToMethod(context =>
        resolver.Resolve<IConnectionManager>().GetHubContext<StockTickerHub>().Clients
        ).WhenInjectedInto<IStockTicker>();
}

За исключением класса NinjectSignalRDependencyResolver, который вы создали, никакого другого кода не нужно добавлять. Импортировано, класс OwinStartup остается неизменным, следующим образом:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR();
    }
}

В приведенном выше примере приведены следующие важные результаты, которые вы задали в своем вопросе:

  • У вас есть только одно ядро ​​Ninject, созданное
  • Ядро и все конфигурации привязки остаются ограниченными в NinjectWebCommon
  • По умолчанию распознаватель SignalR является вашим NinjectSignalRDependencyResolver
  • Зависимость впрыска во все концентраторы SignalR достигается

Надеюсь, это поможет людям.

Ответ 2

Вы пробовали добавить в свой ядро ​​ StockTickerHub?

По умолчанию SignalR использует Activator.CreateInstance для создания концентраторов без каких-либо аргументов конструктора. Если вы хотите вставить свои собственные зависимости в концентратор, вы можете сделать это, зарегистрировав концентратор с зависимым от SignalR.

https://github.com/SignalR/SignalR/blob/2.0.1/src/Microsoft.AspNet.SignalR.Core/Hubs/DefaultHubActivator.cs#L28

Если вы хотите стать действительно творческим, вы можете зарегистрировать свой собственный IHubActivator вместо того, чтобы регистрировать все концентраторы индивидуально.

Я расскажу подробнее о том, как хабы создаются по умолчанию в этом ответе: SignalR с IoC (Castle Windsor) - какое время жизни для концентраторов?

Ответ 3

Существует проблема с областью Singleton. Я не знаю, кто должен здесь винить (Ninject, SignalR, MVC и т.д.), Но он работает, если вы используете ToConstant:

var binding = Bind<IMustBeSingleton>().ToConstant(new MustBeSingleton());

У меня была та же проблема, и я нашел решение: SignalR, WebAPI и MVC, использующие одно и то же ядро ​​зависимостей зависимостей

Я использовал полное решение с MVC, WebAPI и SignalR с использованием того же ядра Ninject: https://drive.google.com/file/d/0B52OsuSSsroNX0I5aWFFb1VrRm8/edit?usp=sharing

В этом примере веб-приложение содержит одну страницу, которая показывает AppDomain и GetHashCode объекта, который должен быть уникальным в трех средах, что дает результат, похожий на:

Dependency Test

Framework   IMySingletonService instance
MVC         AppDomainId:2 / HashCode:5109846
WebAPI      AppDomainId:2 / HashCode:5109846
SignalR     AppDomainId:2 / HashCode:5109846

Надеюсь, это поможет.