4
votes

I want to inject a singleton controller into my hubs using Simple Injector.

I've already tried the following but I'm getting status 500 as response now.

Hub code:

public class EventDataHub : Hub
    {
        private static IEventDataController _dataController;

        public EventDataHub(IEventDataController dataController)
        {
            _dataController = dataController;
        }

        public void Subscribe(string signal)
        {
            _dataController.Subscribe(signal, Context.ConnectionId);
        }
    }

Startup.cs

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

            var container = new Container();

            var hybridLifestyle = Lifestyle.CreateHybrid(
                lifestyleSelector: () => HttpContext.Current != null,
                trueLifestyle: new WebRequestLifestyle(),
                falseLifestyle: new LifetimeScopeLifestyle());

            container.Register<IEventDataController, EventDataController>(Lifestyle.Singleton);
            container.Register<IHub, EventDataHub>(hybridLifestyle);

            container.Verify();

            var activator = new SimpleInjectorHubActivator(container);
            GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => activator);

            app.MapSignalR();
        }
    }

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

The Subscribe method seems to be unreachable from the client side. The hub constructor is executed.

Exception stack:

[MissingMethodException: no parameterless constructor defined for this object]
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +113
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +206 System.Activator.CreateInstance(Type type, Boolean nonPublic) +83 System.Activator.CreateInstance(Type type) +11 Microsoft.AspNet.SignalR.Hubs.DefaultHubActivator.Create(HubDescriptor descriptor) +84
Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(String hubName) +27
Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(IRequest request, HubDescriptor descriptor, String connectionId, StateChangeTracker tracker, Boolean throwIfFailedToCreate) +386
Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(IRequest request, String connectionId, String data) +400
Microsoft.AspNet.SignalR.<>c__DisplayClass64_1.b__5() +34 Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(Func`1 func) +28
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.AspNet.SignalR.Transports.d__40.MoveNext() +742 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58 Microsoft.Owin.Mapping.d__0.MoveNext() +385
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__5.MoveNext() +187 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__2.MoveNext() +185 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +69
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar) +64
System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +380 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

Edit:

Moving the DI configuration from Startup.cs to global.asax throws this exception:

[ArgumentNullException: value cannot be null. Parametername: s] System.IO.StringReader..ctor(String s) +11377176
Microsoft.AspNet.SignalR.Json.JsonSerializerExtensions.Parse(JsonSerializer serializer, String json) +63
Microsoft.AspNet.SignalR.Hubs.HubRequestParser.Parse(String data, JsonSerializer serializer) +21
Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(IRequest request, String connectionId, String data) +40
Microsoft.AspNet.SignalR.<>c__DisplayClass64_1.b__5() +34 Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(Func`1 func) +28
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.AspNet.SignalR.Transports.d__40.MoveNext() +742 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58 Microsoft.Owin.Mapping.d__0.MoveNext() +385
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__5.MoveNext() +187 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +92
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__2.MoveNext() +185 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +69
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar) +65
System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +380 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

1
Please find the raised exception and show all the details of this exception in your question. - Steven
The SignalR application doesn't crash. The exception stack I've included is inside the 500 response. - gerric
From the stacktrace it becomes clear that SignalR is using its DefaultHubActivator instead of your SimpleInjectorHubActivator. - Steven
Please take a look at this discussion to find out how to integrate Simple Injector with SignalR. - Steven
I have difficulties extracting the concluded information. Is it really necessary to implement SimpleInjectorHubDispatcher? Why is registering the custom activator not working? - gerric

1 Answers

3
votes

I finally found the problem:

container.Verify() is breaking the registration of the IHubActivator. So it has to be called either afterwards, or never.

Additionally I removed the container registration for IHub, as it works without now. (I added it because container.Register<IEventDataController, EventDataController>(Lifestyle.Singleton) wasn't working at that time and that fixed it somehow)

So my final code in Startup.cs looks like this:

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

    container.Register<IEventDataController, EventDataController>(Lifestyle.Singleton);

    var activator = new SimpleInjectorHubActivator(container);
    GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => activator);

    app.MapSignalR();
}