SignalR,WebAPI和MVC共享相同的依赖解析器内核

时间:2014-01-08 17:39:29

标签: asp.net-mvc-4 dependency-injection asp.net-web-api ninject signalr

我有一个 ASP.NET MVC 应用 SignalR WebAPI 。该应用程序使用 Ninject 进行依赖注入,但显然SignalR和WebAPI正在获得不同的内核,因此它无法共享应该为所有应用程序共享的单个对象。

我可以在日志中清楚地看到SignalR获取连接请求时如何创建实例,以及WebAPI获取请求时的其他实例。

我想在这三个元素之间共享相同的Ninject内核,所以我可以拥有独特的单例。

这是我到目前为止所做的:

我做的第一件事是创建NinjectModule声明绑定:

public class MyDependencyModule: NinjectModule
{
    public override void Load()
    {
        var binding = Bind<MustBeSingleton>().ToSelf();
        binding.OnActivation((ctx, o) =>
            {
                Debug.Print("Registering item " + o.GetHashCode());
                HostingEnvironment.RegisterObject(o);
            });

        binding.OnDeactivation(o =>
            {
                Debug.Print("Unregistering game connection " + o.GetHashCode());
            });

        binding.InSingletonScope();
    }
}

我还为Ninject创建了一个包装器,以便将其插入WebAPI:

public class NinjectDependencyScope : IDependencyScope
{
    private IResolutionRoot resolver;

    internal NinjectDependencyScope(IResolutionRoot resolver)
    {
        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);
    }
}

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());
    }
}

另外,我为SignalR创建了另一个包装器:

public class SignalRNinjectDependencyResolver : DefaultDependencyResolver
{
    private readonly IKernel _kernel;

    public SignalRNinjectDependencyResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    public override object GetService(Type serviceType)
    {
        return _kernel.TryGet(serviceType) ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType));
    }
}

然后我创建了一个执行所有配置的Ninject内核:

public class ApplicationDependencies:StandardKernel
{
    public ApplicationDependencies()
      :base(new MyDependencyModule())
    {
        System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(this);
        Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver = new SignalRNinjectDependencyResolver(this);
    }
}

MVC应用程序使用NinjectHttpApplication作为基类,所以我指出必须以这种方式使用的内核:

public class MvcApplication : Ninject.Web.Common.NinjectHttpApplication
{
    protected override Ninject.IKernel CreateKernel()
    {
        return new ApplicationDependencies();
    }
}

此外,在SignalR配置中,我指定了解析器:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR<MyPersistentConnection>("/updates", new ConnectionConfiguration()
        {
            Resolver = GlobalHost.DependencyResolver
        });

    }
}

(我也试过没有指定解析器,它也不起作用。)

有什么想法吗?

干杯。

3 个答案:

答案 0 :(得分:2)

为了保持这3件事有效......你应该检查这些参考:

  1. Web API + Ninject http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/
  2. SignalR + Ninject https://github.com/SignalR/SignalR/wiki/Extensibility(最后一部分: 使用ASP.NET MVC时,首先配置SignalR,然后配置ASP.NET MVC)
  3. 对于第二个,我重构了一点,因为我需要SignalR Dependency Resolver的内核

    // Route SignalR.
    GlobalHost.DependencyResolver = NinjectWebCommon.GetSignalrResolver();
    RouteTable.Routes.MapHubs();
    

    我在GetSignalrResolver内定义了NinjectWebCommon,如下所示:

    public static Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver GetSignalrResolver()
    {
        return new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(bootstrapper.Kernel);
    }
    

    注意:有2个不同的DependencyResolverWeb API(1)分配给GlobalConfiguration.Configuration.DependencyResolver,另一个分配给SignalR(2) )分配到GlobalHost.DependencyResolver

答案 1 :(得分:1)

我在另一篇文章中找到了答案:Singleton Scope binding not working as intended

不是绑定为单身,而是必须使用“ToConstant”:

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

我使用相同的依赖注入上下文,使用ASP.NET MVC,WebAPI和SignalR创建了一个简单的演示项目。

https://drive.google.com/file/d/0B52OsuSSsroNX0I5aWFFb1VrRm8/edit?usp=sharing

Web应用程序包含一个页面,该页面显示了一个对象的AppDomain和GetHashCode,该对象在三个框架中应该是唯一的,结果类似于:

Dependency Test

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

其他问题是,Ninject正在处理我的单身因为是IDisposable。我真的不明白为什么会这样,但那是另一场战争。

干杯。

答案 2 :(得分:0)

为了对WebApi和SignalR使用依赖项解析器,您需要实现一个如下所示的类:

    public class NinjectDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver,
    System.Web.Http.Dependencies.IDependencyResolver
{
    public readonly IKernel Kernel;

    public NinjectDependencyResolver(string moduleFilePattern)
        : base()
    {
        Kernel = new StandardKernel();
        Kernel.Load(moduleFilePattern);

    }
    public override object GetService(Type serviceType)
    {
        var service = Kernel.TryGet(serviceType) ?? base.GetService(serviceType);
        return service;
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        IEnumerable<object> services = Kernel.GetAll(serviceType).ToList();
        if (services.IsEmpty())
        {
            services = base.GetServices(serviceType) ?? services;
        }
        return services;
    }

    public System.Web.Http.Dependencies.IDependencyScope BeginScope()
    {
        return this;
    }

    public void Dispose()
    { }
}

然后在您的启动类中,您应该为WebApi和SignalR注册NinjectDependencyResolver,如下所示:

public void Configuration(IAppBuilder app)
{
    var dependencyResolver = new NinjectDependencyResolver("*.dll");

    var httpConfiguration = new HttpConfiguration();
    httpConfiguration.DependencyResolver = dependencyResolver;
    app.UseWebApi(httpConfiguration);

    var hubConfig = new HubConfiguration { Resolver = dependencyResolver };
    app.MapSignalR(hubConfig);
}