0
votes

Trying to set up a SignalR hub (the canonical chat) to run in an OWIN-hosted Nancy app and MassTransit. The MassTransit part works:

public ChatMessageConsumer(IEnumerable<IChatMessageProcessor> messageProcessors,
    IChatHub chatHub, 
    ILogger logger) { ... }

public async Task Consume(ConsumeContext<IChatMessage> context)
{
    _chatHub.BroadcastMessage( ... );
}

Those messages triggered by MassTransit events get to my browser client. The ones triggered from the client never do:

public class ChatHub : Hub, IChatHub 
{
    public ChatHub(ILifetimeScope lifetimeScope) 
    {
        _hubLifetimeScope = lifetimeScope.BeginLifetimeScope();
        // snip ...
    }
    // snip ...
}

public void Send(string name, string message)
{
    // publish message on bus
    _bus.Publish<IChatMessage>(/* snip ... */);

    _hubInstanceContext.Clients.All.BroadcastMessage(/* ... */);
}

public void BroadcastMessage(string service, string message, string correlationId) { /* snip ... */ }

As best as I can tell, the framework is looking for a parameterless constructor, which I don't have (and obviously don't want). In my startup I create a configuration, register the Autofac resolver as the dependency resolver, and start SignalR with the configuration. Nancy is started using an Autofac bootstrapper with the same container.

public void Configuration(IAppBuilder app)
{
    var container = ResolveDependencies();
    var hubConfiguration = new HubConfiguration
    {
        EnableDetailedErrors = true,
        Resolver = container.Resolve<IDependencyResolver>()
    };

    app.UseAutofacMiddleware(container);
    app.MapSignalR(hubConfiguration);
    app.Map("/site", sb => sb.UseNancy(options => options.Bootstrapper = new ChatBootstrapper(container)));
    // snip MassTransit bus start/stop, etc. ...
}

static IContainer ResolveDependencies()
{
    var builder = new ContainerBuilder();

    // snip MassTransit consumers, etc. ...

    builder.RegisterType<ChatHub>().As<IChatHub>().ExternallyOwned().SingleInstance();
    builder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
    builder.Register(i => i.Resolve<IDependencyResolver>()
            .Resolve<IConnectionManager>()
            .GetHubContext<ChatHub, IChatHub>())
        .SingleInstance()
        .ExternallyOwned();
    // snip Logger registrations, etc. ...
}

Nancy is working (that's how I post events to MassTransit). MassTransit is working -- events are consumed and are able to broadcast SignalR events that are received by the client. SignalR is partially working -- I've obviously got a connection to a hub because I can receive messages broadcast from MassTransit consumers, but when I try to send a message from a client, I get a 500 internal server error. If I add a parameterless constructor to the hub, when I try to send from the client, that constructor is called, leading me to think that there's something wrong with how I've setup Autofac for SignalR. In that case, when the Send method is called, the _hubInstanceContext and _bus members, that should have been set via DI in the constructor are null. I've tried adding a IHubContext<IMyHub> property, as suggested in https://stackoverflow.com/a/36476106/173225, but that is never set, supporting my suspicion that DI isn't being used at all in this case.

I've reviewed the Autofac docs, many questions here on SO and several blog posts, but can't see what I might be missing. I should also note I did have the client chat working before adding Autofac to the mix (i.e. I could send a message and receive a broadcastMessage).

Any suggestions?

Edit: adding callstack in parameterless constructor:

>   Chat.Service.exe!Chat.Service.Hubs.ChatHub.ChatHub() Line 25    C#
    mscorlib.dll!System.Activator.CreateInstance(System.Type type, bool nonPublic)  Unknown
    mscorlib.dll!System.Activator.CreateInstance(System.Type type)  Unknown
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.DefaultHubActivator.Create(Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor) Line 29  C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(string hubName) Line 89    C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(Microsoft.AspNet.SignalR.IRequest request, Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor, string connectionId, Microsoft.AspNet.SignalR.Hubs.StateChangeTracker tracker, bool throwIfFailedToCreate) Line 455  C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(Microsoft.AspNet.SignalR.IRequest request, string connectionId, string data) Line 180  C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__5() Line 282   C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(System.Func<System.Threading.Tasks.Task> func) Line 771   C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__4(string data) Line 282    C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.ForeverTransport.ProcessSendRequest() Line 151    C#
    [Resuming Async Method] 
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)  Unknown
    mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()    Unknown
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__72_0(System.Action f) Line 1036 C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__71_0(System.Action<System.__Canon> f, System.__Canon state) Line 1028   C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.__Canon, System.__Canon, System.__Canon>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Func<System.__Canon, System.__Canon, System.__Canon> func, System.__Canon arg1, System.__Canon arg2) Line 1009 C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.Action>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action<System.Action> action, System.Action arg) Line 1031  C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action action) Line 1037   C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAwaiterHelper.PreserveCultureUnsafeOnCompleted.AnonymousMethod__0() Line 48  C#
    mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask)    Unknown
    mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations()  Unknown
    mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown
    mscorlib.dll!System.Threading.Tasks.Task<System.__Canon>.TrySetResult(System.__Canon result)    Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Microsoft.AspNet.SignalR.Hosting.INameValueCollection>.SetResult(Microsoft.AspNet.SignalR.Hosting.INameValueCollection result)  Unknown
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Owin.ServerRequest.ReadForm() Line 111   C#
    [Resuming Async Method] 
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)  Unknown
    mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()    Unknown
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__72_0(System.Action f) Line 1036 C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__71_0(System.Action<System.__Canon> f, System.__Canon state) Line 1028   C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.__Canon, System.__Canon, System.__Canon>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Func<System.__Canon, System.__Canon, System.__Canon> func, System.__Canon arg1, System.__Canon arg2) Line 1009 C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.Action>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action<System.Action> action, System.Action arg) Line 1031  C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action action) Line 1037   C#
    Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAwaiterHelper.PreserveCultureUnsafeOnCompleted.AnonymousMethod__0() Line 48  C#
    mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()    Unknown
    mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()    Unknown
    mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unknown
    [Async Call]    
    Microsoft.Owin.dll!Microsoft.Owin.Mapping.MapMiddleware.Invoke(System.Collections.Generic.IDictionary<string, object> environment) Line 64  C#
    [Async Call]    
    Autofac.Integration.Owin.dll!Owin.AutofacAppBuilderExtensions.RegisterAutofacLifetimeScopeInjector.AnonymousMethod__0(Microsoft.Owin.IOwinContext context, System.Func<System.Threading.Tasks.Task> next) Line 333  C#
    [Async Call]    
    Microsoft.Owin.Host.HttpListener.dll!Microsoft.Owin.Host.HttpListener.OwinHttpListener.ProcessRequestAsync(System.Net.HttpListenerContext context) Line 262 C#
    [Async Call]    
    Microsoft.Owin.Host.HttpListener.dll!Microsoft.Owin.Host.HttpListener.OwinHttpListener.ProcessRequestsAsync() Line 244  C#
1
Have you installed and initialised the Autofac SignalR integration?Alexey Zimarev
You mean Install-Package Autofac.SignalR and then following docs.autofac.org/en/latest/integration/…? AFAIK I have followed the instructions in the docs. That's why I included my startup Configuration code, in case I missed something.Colin Young
Do you have any stack trace ? if not, could you add a breakpoint on the parameterless constructor and share the full call stack ?Cyril Durand
could you check that config.Resolver is the correct one (ie AutofacDependencyResolver) ?Cyril Durand
hubConfiguration.Resolver.GetType() yields {Name = "AutofacDependencyResolver" FullName = "Autofac.Integration.SignalR.AutofacDependencyResolver"}Colin Young

1 Answers

0
votes

I needed to change:

builder.RegisterType<ChatHub>().As<IChatHub>().ExternallyOwned().SingleInstance();

to:

builder.RegisterType<ChatHub>().ExternallyOwned();
builder.Register(c => c.Resolve<ChatHub>()).As<IChatHub>();

At this point I'm not entirely sure why, but I'm going to do some digging in the Autofac source and update this answer.

I had previously tried many variations, with and without SingleInstance and AsImplementedInterfaces, etc., but nothing worked until I stripped it back to exactly what is presented in the Autofac docs.