2015-08-08 36 views
6

根据this的帖子,应该可以将每个Web请求的依赖关系注入到SignalR集线器中(虽然有一些限制,例如OnDisconnected()方法的问题)。在我的情况下,它是ASP Web API(不是MVC),并且由于某种原因它不起作用。SignalR集线器中简单的喷油器对web-api-request的依赖关系

以下是有关部分:

container.RegisterWebApiControllers(httpConfiguration); 

container.RegisterWebApiRequest<DbContext, MyDbContext>(); 
container.RegisterWebApiRequest<ISampleRepository, SampleRepository>(); //DbContext injected to SampleRepository 


//Enable injections to SignalR Hubs 
var activator = new SimpleInjectorHubActivator(container); 
GlobalHost.DependencyResolver.Register(typeof(IHubActivator),() => activator); 

该类使得有可能注入到集线器:

public class SimpleInjectorHubActivator : IHubActivator 
     { 
      private readonly Container _container; 

      public SimpleInjectorHubActivator(Container container) 
      { 
       _container = container; 
      } 

      public IHub Create(HubDescriptor descriptor) 
      { 
       return (IHub)_container.GetInstance(descriptor.HubType); 
      } 
} 

和轮毂本身:

[HubName("sample")] 
public class SampleHub : Hub 
    { 

     public ActiveBetsHub(ISampleRepository repository) 
     { 
     } 

     //Irrelevant methods here. OnDisconnected() NOT implemented! 
    } 

在此设置下,我得到异常:

No registration for type SampleHub could be found and 
an implicit registration could not be made. 
The ISampleRepository is registered as 'Web API Request' 
lifestyle, but the instance is requested outside the context of a Web API Request. 

这是预期的,据我所知。但是我得到完全相同的异常,当我改变仓库的生活方式瞬态:

var transientHybrid = Lifestyle.CreateHybrid(() => HttpContext.Current != null, new WebApiRequestLifestyle(), Lifestyle.Transient); 
    container.Register<ISampleRepository, SampleRepository>(transientHybrid); 

我怀疑问题可能在于HttpContext.Current != null检查不工作的Web API的方式为MVC相同。

SignalR 2.2

简单的喷油器2.8.3

我怎么错过?

UPDATE:

这是SignalR如何创造集线器堆栈跟踪:

at SimpleInjector.InstanceProducer.GetInstance() 
    at SimpleInjector.Container.GetInstance(Type serviceType) 
    at MyWebAbi.WebApiApplication.SimpleInjectorHubActivator.Create(HubDescriptor descriptor) in Global.asax.cs:line 108 
    at Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(String hubName) 
    at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(IRequest request, HubDescriptor descriptor, String connectionId, StateChangeTracker tracker, Boolean throwIfFailedToCreate) 

所以,正确的解决办法是使用ExecutionContextScope一个集线器,但这个范围必须明确地关闭它使事情变得更加复杂......

回答

7

你的混合生活方式的定义是不正确的。 WebApiRequestLifestyle不以任何方式依赖于HttpContext,因此检查HttpContext.Current != null是否不起作用。你将不得不通过调用container.GetCurrentExecutionContextScope()检查,如果有一个活动的Web API请求生活方式范围(或执行上下文范围,这是基本相同的):

var transientHybrid = Lifestyle.CreateHybrid(
    () => container.GetCurrentExecutionContextScope() != null, 
    new WebApiRequestLifestyle(), 
    Lifestyle.Transient); 

确实注意到然而,你应该非常小心组成一种混合生活方式的范围生活方式和短暂的,因为这将容易产生错误的结果。这实际上是一些DI库的默认行为,但这是一个design flaw IMO。我假设你非常有意识地注册了你的MyDbContext范围的生活方式,因为你需要确保在整个请求中使用相同的实例。使用Transient生活方式意味着您可能在请求期间获得多个MyDbContext。这可能不成问题,因为在您的集线器中,您目前可能只有一个对MyDbContext的引用,但是当您的对象图更改并添加第二个参考MyDbContext时,您的代码可能会中断。

所以相反,我会建议不要使用这种生活方式的组合。相反,只需使用WebApiRequestLifestyleExecutionContextScopeLifestyle(它们是相同的),并确保在集线器解析之前启动此类执行上下文范围。

顺便说一下,不要忘记在Simple Injector中明确注册集线器。这允许简单注射器为您分析包括您的中心类的完整对象图。

+0

非常感谢您的回答@Steven。当你说“并确保这样一个执行上下文范围是在你的集线器解决之前启动的。”你的意思是使用container.BeginExecutionContextScope()?由于这个范围需要被明确处理,如果没有使用CommandHandler模​​式,你会如何建议实现这个部分。也应该被Hubs注册为WebApiRequest Lifestyles?谢谢! –

+0

@IljaS。我无法真正回答这个问题。我不知道你的实现是怎么样的,在什么时候你得到这个堆栈跟踪(以及我的SignalR经验缺乏)。尝试使用更多的代码和完整的堆栈跟踪来更新您的问题我会看一看。 – Steven

+0

实际上并没有太多的实施。为了简单起见,我们可以说它是非常简单的WebApi应用程序,带有1个注入了DbContext的Repository类。 1 Hub,我想注入这个Repository类。那就是它。我添加了SignalR调用Hub创建的Stack trace。我不知道还有什么要包括的。 –

2

最近,我遇到了同样的问题,发现了下面的工作非常好,希望这能帮助别人:

public class SignalRDependencyResolver : DefaultDependencyResolver 
{ 
    public SignalRDependencyResolver(IServiceProvider serviceProvider) 
    { 
     _serviceProvider = serviceProvider; 
    } 

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

    public override IEnumerable<object> GetServices(Type serviceType) 
    { 
     var @this = (IEnumerable<object>) _serviceProvider.GetService(typeof (IEnumerable<>).MakeGenericType(serviceType)); 

     var @base = base.GetServices(serviceType); 

     return @this == null ? @base : @base == null ? @this : @this.Concat(@base); 
    } 

    private readonly IServiceProvider _serviceProvider; 
} 

public class SignalRHubDispatcher : HubDispatcher 
{ 
    public SignalRHubDispatcher(Container container, HubConfiguration configuration) : base(configuration) 
    { 
     _container = container; 
    } 

    protected override Task OnConnected(IRequest request, string connectionId) 
    { 
     return Invoke(() => base.OnConnected(request, connectionId)); 
    } 

    protected override Task OnReceived(IRequest request, string connectionId, string data) 
    { 
     return Invoke(() => base.OnReceived(request, connectionId, data)); 
    } 

    protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled) 
    { 
     return Invoke(() => base.OnDisconnected(request, connectionId, stopCalled)); 
    } 

    protected override Task OnReconnected(IRequest request, string connectionId) 
    { 
     return Invoke(() => base.OnReconnected(request, connectionId)); 
    } 

    private async Task Invoke(Func<Task> method) 
    { 
     using (_container.BeginExecutionContextScope()) 
      await method(); 
    } 

    private readonly Container _container; 
} 

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     var container = new Container(); 

     container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle(); 

     container.Register<DbContext, MyDbContext>(Lifestyle.Scoped); 
     container.Register<ISampleRepository, SampleRepository>(Lifestyle.Scoped); 

     // if you want to use the same container in WebApi don't forget to add 
     app.Use(async (context, next) => { 
      using (container.BeginExecutionContextScope()) 
       await next(); 
     }); 

     // ... configure web api 

     var config = new HubConfiguration 
     { 
      Resolver = new SignalRDependencyResolver(container) 
     } 

     // ... configure the rest of SignalR 

     // pass SignalRHubDispatcher 
     app.MapSignalR<SignalRHubDispatcher>("/signalr", config); 
    } 
} 
+0

经历了几次这个问题后,我相信这个答案与当前的SignalR/SI设置最为相关(截至2016年7月) - 既好又简单。感谢您在SO注册! – keithl8041

+0

嗨Serg。你有Git吗?我遇到了一个很大的问题。 谢谢 – rsegovia

相关问题