SignalR .NET Client Invoke throws an exception

2019-07-09 20:24发布

问题:

I'm trying to get a console application working with the SignalR .Net Client but I'm getting an error when I try to invoke a method on the Hub. Here's my code for the console app:

static void Main(string[] args)
{            
    var connection = new HubConnection("http://localhost/SignalRTest");
    var myHub = connection.CreateProxy("SignalRTest.Classes.service");

    myHub.On<string>("addMessage", text =>
    {
        Console.WriteLine(text);
    });

    connection.Start().ContinueWith(task =>
    {
        if (task.IsFaulted)
        {   
            Console.WriteLine("There was an error opening the connection: {0}", task.Exception.GetBaseException());
        }
        else {                        
            Console.WriteLine("Connected.");
        }
   }).Wait();


   myHub.Invoke("Send", "Message from console.").ContinueWith(task => {
       if (task.IsFaulted)
       {                
           Console.WriteLine("There was an error calling Send: {0}", task.Exception.GetBaseException());
       }
       else
       {
           Console.WriteLine("Send complete.");
       }

   });


   Console.ReadLine();

}

Here is the Hub from the Server:

[HubName("service")]
public class ServiceHub : Hub
{
    public void Send(string message)
    {
        // Call the addMessage method on all clients
        Clients.addMessage(message);
    }
}

I assume that the console app is connecting correctly because it writes out "Connected." But when it tries to call the Send method on the server, I get the following error:

System.Net.WebException: The remote server returned an error: (500) Internal Server Error. at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) at SignalR.HttpHelper.<>c_DisplayClass2.b_0(IAsyncResult ar) at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endMethod, TaskCompletionSource`1 tcs)

Can anyone tell me what I'm doing wrong? Thanks.

回答1:

This issue appears to be caused by using an invalid hub name (when using CreateProxy). It is strange that the start method doesn't fail but i just tested this and got the same behavior as you using a non-existent hub name.



回答2:

You have not added the SignalR.hosting.AspNet dll, so this error is created. So, first include the dll with SignalR.Hosting.AspNet on the server.



回答3:

For me this was a result of my SignalR hub requiring a dependency injection parameter. The error gets quite well hidden, even with the ShowDetailedErrors being set to true it was obscured except within the Server logs of Azure.

The error you get is something like this:

System.MissingMethodException:
   at System.RuntimeTypeHandle.CreateInstance (mscorlib, Version=4.0.0.0, 
   Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.RuntimeType.CreateInstanceSlow (mscorlib, Version=4.0.0.0, 
   Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.RuntimeType.CreateInstanceDefaultCtor (mscorlib, Version=4.0.0.0, 
   at Microsoft.AspNet.SignalR.Hubs.DefaultHubActivator.Create 
   (Microsoft.AspNet.SignalR.Core, Version=2.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)

As you can see I was using the 'DefaultHubActivator' which doesn't know how to manage injector parameters. So I followed this fine advice which taught me how to do the injection using Unity.

The basics are you need to write your own hub activator, but you can inherit from the default one and just extend it to avoid having to register the dozens of dependencies that SignalR has.

 public class SignalRUnityDependencyResolver : DefaultDependencyResolver
{
    private readonly IUnityContainer _container;
    public SignalRUnityDependencyResolver(IUnityContainer container)
    {
        _container = container ?? throw new ArgumentNullException(nameof(container));
    }

    public override object GetService(Type serviceType)
    {
        return _container.IsRegistered(serviceType) ? _container.Resolve(serviceType) : base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.IsRegistered(serviceType) ? _container.ResolveAll(serviceType) : base.GetServices(serviceType);
    }
}

After which you set it up in your Startup

app.Map("/signalr", map =>
        {
            var hubConfiguration = new HubConfiguration()
            {
                EnableJSONP = true,
                EnableDetailedErrors = true,
                Resolver = new SignalRUnityDependencyResolver(UnityConfig.GetContainer())
            };
            map.RunSignalR(hubConfiguration);
            logger.WriteInfo("Started signalr.");
        });

Finally registering your hub with Unity

container.RegisterType<MyHub>(new InjectionFactory((obj) => new MyHub(obj.Resolve<ILogger>())));