You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server场景下如何将类文件数据传递到Razor页面

实现方案

你需要用「单例中间件+事件通知」的模式实现需求,完全不需要修改Server类的原有业务逻辑,中间件作为纯中转层解耦服务端和UI:

步骤1:注册全局单例服务

首先在Program.cs中注册中间件和TCP服务为全局单例,保证整个程序只有一份实例,数据全局统一:

// 其他服务注册逻辑保留
builder.Services.AddSingleton<Middleware>();
builder.Services.AddSingleton(sp => new Server(8600, sp.GetRequiredService<Middleware>()));

var app = builder.Build();

// 在这里启动TCP服务,不要放在页面里启动,避免多用户访问重复启动
var server = app.Services.GetRequiredService<Server>();
new Thread(() => server.Start()).Start();

// 原有app.Run()等逻辑保留

步骤2:修改中间件类,新增事件通知

中间件内部存储UI需要的设备数据,同时定义事件通知订阅者(也就是Razor页面)更新:

public class Middleware
{
    // 存储UI需要的设备列表,直接给页面读取
    public List<DeviceInfo> ConnectedDevices { get; } = new();
    // 设备添加事件,新设备接入时触发
    public event Action? OnDeviceChanged;
    // 错误通知事件,服务端报错时触发
    public event Action<string>? OnErrorOccurred;

    public void AddDeviceEntry(string device, string endpoint)
    {
        var ipPort = endpoint.Split(':');
        ConnectedDevices.Add(new DeviceInfo
        {
            DeviceName = device,
            Ip = ipPort[0],
            Port = ipPort[1]
        });
        // 通知所有订阅者数据更新
        OnDeviceChanged?.Invoke();
    }

    public void ErrorMessage(string msg)
    {
        OnErrorOccurred?.Invoke(msg);
    }
}

// 设备信息DTO,解耦UI和TCP服务逻辑
public class DeviceInfo
{
    public string DeviceName { get; set; } = string.Empty;
    public string Ip { get; set; } = string.Empty;
    public string Port { get; set; } = string.Empty;
}

步骤3:修改Index.razor,订阅中间件事件

页面只依赖中间件,不直接访问Server类,收到事件通知后调度到UI线程刷新界面:

@page "/"
@inject Middleware Mid
@implements IDisposable

@* 错误提示区域 *@
@if (!string.IsNullOrEmpty(ErrorMsg))
{
    <div class="alert alert-danger mt-2">@ErrorMsg</div>
}

<table class="table">
  <thead>
        <tr>
            <th scope="col">Dispositivos</th>
            <th scope="col">IP</th>
            <th scope="col">Porta</th>
        </tr>
  </thead>
  <tbody>
        <tr>
            <td>Servidor</td>
            <td>127.0.0.1</td>
            <td>8600</td>
        </tr>
        @foreach (var device in Mid.ConnectedDevices)
        {
            <tr>
                <td>@device.DeviceName</td>
                <td>@device.Ip</td>
                <td>@device.Port</td>
            </tr>
        }
  </tbody>
</table>

@code {
    private string ErrorMsg { get; set; } = string.Empty;

    protected override void OnInitialized()
    {
        // 订阅设备更新事件
        Mid.OnDeviceChanged += () =>
        {
            // 后台线程触发的事件必须调度到UI同步上下文再刷新
            InvokeAsync(StateHasChanged);
        };
        // 订阅错误通知事件
        Mid.OnErrorOccurred += msg =>
        {
            InvokeAsync(() =>
            {
                ErrorMsg = msg;
                StateHasChanged();
            });
        };
    }

    // 页面销毁时取消订阅,避免内存泄漏
    public void Dispose()
    {
        Mid.OnDeviceChanged -= () => InvokeAsync(StateHasChanged);
        Mid.OnErrorOccurred -= _ => InvokeAsync(StateHasChanged);
    }
}

原理说明

  • 完全符合你要求的三层结构:Server类→中间件→UI页面,Server不需要感知UI存在,UI也不依赖Server的内部实现
  • 后台线程触发的事件必须通过InvokeAsync调度到Blazor的UI同步上下文,否则直接调用StateHasChanged会报线程错误
  • 事件模式天然支持多页面订阅,如果后续需要其他页面也接收设备/错误通知,直接订阅中间件的对应事件即可

内容的提问来源于stack exchange,提问作者jeanvr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 02:45:03