skip to Main Content

Pretty fresh to the ASP.NET / ASP.NET Core environments (typically work in WinForms using DevExpress), and having a bit of a problem trying to understand and implement a SignalR server that calls methods on the client. From my understanding of the "Client Results" section of this Using Hubs in SignalR article, I should be able to call some methods on the client from the server. This works in most cases; however, when I try to call a method on the client that takes in parameters and returns a result, I get an error: System.Exception: 'Client didn't provide a result.', which I cannot seem to find much info on.

My server in this case is a basic ASP.NET Core application targeting .NET 7 (so <TargetFramework>net7.0</TargetFramework> in the .csproj), and my client is a WinForms application targeting .NET 7 Windows (net7.0-windows) and using the Microsoft.AspNetCore.SignalR.Client NuGet package (v 7.0.7 as of writing).

My intention is to eventually have a WinForms "SignalR Server" that can communicate with a variety of clients, but use one connected client as the "Primary Client". I haven’t built any of that code yet, so I’m just using my first connected client as the "Primary Client".

Here’s my server-side code (using a strongly-typed hub):

public interface IStrongServer
{
    Task<Boolean> TestBoolean();
    Task<Boolean> TestBooleanWithParameters(string toSend);
}

public class StrongServerHub : Hub<IStrongServer>
{
    private readonly StrongServer _StrongServer;

    public StrongServerHub(StrongServer nStrongServer)
    {
        _StrongServer = nStrongServer;
    }

    public override async Task OnConnectedAsync()
    {
        _StrongServer.AssignPrimaryConnection(this.Context.ConnectionId);
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        _StrongServer.DisconnectPrimaryConnection(this.Context.ConnectionId);
        await base.OnDisconnectedAsync(exception);
    }
}

public class StrongServer : IStrongServer
{
    private string PrimaryConnectionId = "";
    private bool PrimaryConnected = false;

    private IHubContext<StrongServerHub, IStrongServer> Hub
    {
        get;
        set;
    }

    public StrongServer(IHubContext<StrongServerHub, IStrongServer> hub)
    {
        Hub = hub;
    }

    public void AssignPrimaryConnection(string connectionId)
    {
        if (String.IsNullOrEmpty(PrimaryConnectionId))
        {
            PrimaryConnectionId = connectionId;
            PrimaryConnected = true;
        }
    }

    public void DisconnectPrimaryConnection(string connectionId)
    {
        if (!String.IsNullOrEmpty(PrimaryConnectionId) && String.Equals(PrimaryConnectionId, connectionId))
        {
            PrimaryConnectionId = "";
            PrimaryConnected = false;
        }
    }

    public async Task<bool> TestBoolean()
    {
        return await Hub.Clients.Client(PrimaryConnectionId).TestBoolean();
    }

    public async Task<bool> TestBooleanWithParameters(string ToSend)
    {
        return await Hub.Clients.Client(PrimaryConnectionId).TestBooleanWithParameters(ToSend);
    }
}

public class SignalRHost
{
    public StrongServer Server { get; set; }

    public void Run()
    {
        var builder = WebApplication.CreateBuilder();

        // Add services to the container.
        builder.Services.AddRazorPages();
        builder.Services.AddSignalR();
        builder.Services.AddSingleton<StrongServer>();

        var app = builder.Build();

        // Configure the HTTP request pipeline.
        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.MapRazorPages();
        app.MapHub<StrongServerHub>("/strong");

        Server = app.Services.GetService<StrongServer>();

        app.RunAsync();

    }
}

// Then in Program.cs, I'd do:
SignalRHost host = new SignalRHost();
host.Run();

bool returnedBoolean = await host.Server.TestBoolean();
bool returnedBooleanWithParameters = await host.Server.TestBooleanWithParameters("Hello World!");

And here’s my client-side code:

public interface ITestClient
{
    bool TestBoolean();
    bool TestBooleanWithParameters(string test);
}

using System.Diagnostics;
public class SignalRClient : ITestClient
{
    HubConnection TestConnection = null;

    public async void Init()
    {
        TestConnection = new HubConnectionBuilder().WithUrl("http://localhost:5035/strong").Build(); // The port 5035 is what my machine used.

        TestConnection.On(nameof(TestBoolean), TestBoolean);
        TestConnection.On<string>(nameof(TestBooleanWithParameters), (p1) => TestBooleanWithParameters(p1));

        await TestConnection.StartAsync();
    }

    public bool TestBoolean()
    {
        return true;
    }

    public bool TestBooleanWithParameters(string test)
    {
        Debug.WriteLine($"TestBooleanReturn: {test}"); ;
        return true;
    }
}

internal static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        SignalRClient client = new SignalRClient();
        client.Init();
        
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }
}

Again, the differences between ASP.NET and ASP.NET Core are confusing enough to me, so my first thought was that it could be a

TLDR: Is expecting results from a client not possible if the client method has parameters? Am I missing something obvious?

2

Answers


  1. Chosen as BEST ANSWER

    @jdweng's comment about the HTTP Request pointed me in the direction of logging the communication between the client and the server. I added both server-side and client-side logging as found in this Logging and Diagnostics article and found that I was getting an error on the client-side: Microsoft.AspNetCore.SignalR.Client.HubConnection: Warning: Failed to find a value returning handler for TestBooleanWithParameters.

    After seeing some other comments across StackOverflow and Github, I realized that I had to change the following line:

    TestConnection.On<string>(nameof(TestBooleanWithParameters), (p1) => TestBooleanWithParameters(p1));
    

    And change it to:

    TestConnection.On(nameof(TestBooleanWithParameters), (String p1) => TestBooleanWithParameters(p1));
    

    Now everything is working and I'm not longer receiving errors.


  2. The error means that a response was not returned on the connection, so it was not completed. The reasons can be various. However your error should be TestConnection.On<string>.

    More precisely the error is <string>

    After .On, you need to delete <string>. Then continue with your code in the parenthesis

    Solution

    TestConnection.On(nameof(TestBooleanWithParameters), (String p1) => TestBooleanWithParameters(p1));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search