【问题标题】:SignalR, WebAPI and MVC sharing the same dependency resolver kernelSignalR、WebAPI 和 MVC 共享相同的依赖解析器内核
【发布时间】:2015-04-27 15:00:34
【问题描述】:

我有一个带有 SignalRWebAPIASP.NET MVC 应用程序。该应用程序使用 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
        });

    }
}

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

有什么想法吗?

干杯。

【问题讨论】:

  • 前段时间我遇到了同样的问题,我切换到 Unity for Web App / Web API 以使用相同的解析器内核。我知道你也可以在 Ninject 中实现这一点,但我发现我必须使用 NinjectMVC3 和 NinjectWebAPI 进行一些黑客攻击。希望这会有所帮助!

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


【解决方案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】:

    为了保持这 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)

    对于第二个,我稍微重构了一下,因为我需要SignalR Dependency Resolver 的内核

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

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

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

    注意:有两种不同的DependencyResolver:一种用于Web API(1)分配给GlobalConfiguration.Configuration.DependencyResolver,另一种用于SignalR(2)分配给GlobalHost.DependencyResolver

    【讨论】:

      【解决方案3】:

      要为 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);
      }
      

      【讨论】:

        猜你喜欢
        • 2014-02-26
        • 1970-01-01
        • 2013-04-16
        • 2013-06-15
        • 1970-01-01
        • 2012-03-31
        • 1970-01-01
        • 2013-04-12
        • 1970-01-01
        相关资源
        最近更新 更多