非常熟悉ASP.NET/ASP.NET核心环境(通常使用DevExpress在WinForms中工作),并且在try 理解和实现调用客户端方法的SignalR服务器时遇到一些问题.根据我对这Using Hubs in SignalR article的"客户端结果"部分的理解,我应该能够从服务器调用客户端上的一些方法.这在大多数情况下都有效;但是,当我try 在客户端调用一个接受参数并返回结果的方法时,我得到了一个错误:System.Exception: 'Client didn't provide a result.',我似乎找不到关于它的更多信息.

在本例中,我的服务器是一个面向.NET 7(.csproj中为<TargetFramework>net7.0</TargetFramework>)的基本ASP.NET核心应用程序,而我的客户端是一个面向.NET 7 Windows(net7.0-windows)并使用Microsoft.AspNetCore.SignalR.Client NuGet包( compose 本文时为7.0.7)的WinForms应用程序.

我的目标是最终拥有一个WinForms"SignalR服务器",它可以与各种客户端通信,但使用一个连接的客户端作为"主客户端".我还没有构建任何代码,所以我只是将我的第一个连接的客户端用作"主客户端".

以下是我的服务器端代码(使用强类型集线器):

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!");

下面是我的客户端代码:

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

同样,ASP.NET和ASP.NET Core之间的差异让我非常困惑,所以我的第一个 idea 是它可能是一个

TLDR:如果客户端方法有参数,来自客户端的预期结果是不可能的吗?我是不是漏掉了什么明显的东西?

推荐答案

@jdweng关于HTTP请求的 comments 将我引向了记录客户端和服务器之间的通信的方向.我同时添加了服务器端和客户端日志(log)记录as found in this Logging and Diagnostics article,并发现在客户端收到错误:Microsoft.AspNetCore.SignalR.Client.HubConnection: Warning: Failed to find a value returning handler for TestBooleanWithParameters.

在看到StackOverflow和Github上的其他一些 comments 后,我意识到我必须更改以下行:

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

并将其更改为:

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

现在一切都正常了,我不再收到错误.

Csharp相关问答推荐

我们应该如何在IHostedService中使用按请求的GbContent实例?

.NET最小API映射将T参数列表为[FromQuery]

ASP.NET MVC购物车数量更新并从购物车中删除项目

一小时后,自定义缓存停止在App Insight中保存

如何分配对象后的class的属性?

在C#中,DirectoryEntry返回空AuditRules集合,即使审计规则确实存在

如何在不考虑年份的情况下判断日期时间是否在某个日期范围内?

自动映射程序在GroupBy之后使用项目

在静态模式下实例化配置

UWP应用程序try 将打包的本机.exe文件加载为C#程序集

try 在.Net核心身份注册页面中使用AJAX,但没有成功..NET Core 5.0 Razor页面应用程序

C#普罗米修斯指标

当索引和外键是不同的数据类型时,如何设置导航属性?

如何更改新创建的实例的变量?

如何在microsoft.clearscript.v8的jsondata中使用Linq

如何实现有条件的自定义Json转换器隐藏属性

MudBlazor Textfield已禁用,但其验证工作正常

为什么Swashbakle/Swagger在参数中包含变量名?

如何在单击按钮后多次异步更新标签

为什么我的UserControl没有加载到我的主窗口中?