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

如何为特定用户实现ASP.NET Core后台任务及专属UI更新

实现方案:ASP.NET Core MVC/Razor Pages 专属用户后台任务 + SignalR 实时更新

核心思路

用 SignalR 实现实时推送 替代 AJAX 轮询,搭配 用户专属任务管理器 隔离不同用户的后台任务,确保更新只推送给对应用户,且用户跳转页面返回后能续接同一任务的状态。


步骤1:配置 SignalR 实时通信

SignalR 是 ASP.NET Core 官方的实时通信框架,能直接将后端更新推送到前端,比 AJAX 轮询更高效。

1.1 创建 SignalR Hub

这个 Hub 负责管理用户连接,关联用户ID和SignalR连接ID:

using Microsoft.AspNetCore.SignalR;
using System.Security.Claims;

public class TaskUpdateHub : Hub
{
    private readonly UserTaskManager _taskManager;

    public TaskUpdateHub(UserTaskManager taskManager)
    {
        _taskManager = taskManager;
    }

    public override async Task OnConnectedAsync()
    {
        var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userId))
        {
            await _taskManager.AddUserConnection(userId, Context.ConnectionId);
        }
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userId))
        {
            await _taskManager.RemoveUserConnection(userId, Context.ConnectionId);
        }
        await base.OnDisconnectedAsync(exception);
    }
}

1.2 注册 SignalR 服务

在 Program.cs 中添加SignalR配置:

var builder = WebApplication.CreateBuilder(args);

// 注册SignalR服务
builder.Services.AddSignalR();
// 注册用户任务管理器(单例,全局唯一)
builder.Services.AddSingleton<UserTaskManager>();

// 注册MVC/Razor Pages服务
builder.Services.AddRazorPages();
builder.Services.AddControllers();

var app = builder.Build();

// 配置SignalR端点
app.MapHub<TaskUpdateHub>("/taskUpdateHub");

// 其他中间件配置...
app.MapRazorPages();
app.MapControllers();

app.Run();

步骤2:实现用户专属任务管理器

创建一个单例服务 UserTaskManager,用来跟踪每个用户的后台任务和连接,确保任务与用户绑定:

using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;

public class UserTaskManager
{
    private readonly IHubContext<TaskUpdateHub> _hubContext;
    // 存储用户ID与任务的映射
    private readonly ConcurrentDictionary<string, UserBackgroundTask> _userTasks = new();
    // 存储用户ID与SignalR连接ID的映射(支持多设备登录)
    private readonly ConcurrentDictionary<string, List<string>> _userConnections = new();

    public UserTaskManager(IHubContext<TaskUpdateHub> hubContext)
    {
        _hubContext = hubContext;
    }

    // 启动用户专属任务
    public async Task StartUserTask(string userId)
    {
        if (_userTasks.ContainsKey(userId)) return;

        var cts = new CancellationTokenSource();
        var task = new UserBackgroundTask(userId, cts.Token, this);
        _userTasks.TryAdd(userId, task);
        // 后台异步执行任务,不阻塞主线程
        _ = task.RunAsync();
        await Task.CompletedTask;
    }

    // 停止用户任务
    public void StopUserTask(string userId)
    {
        if (_userTasks.TryRemove(userId, out var task))
        {
            task.Cancel();
        }
    }

    // 添加用户连接
    public async Task AddUserConnection(string userId, string connectionId)
    {
        var connections = _userConnections.GetOrAdd(userId, _ => new List<string>());
        lock (connections)
        {
            if (!connections.Contains(connectionId))
                connections.Add(connectionId);
        }

        // 用户重新连接时,发送当前任务状态
        if (_userTasks.TryGetValue(userId, out var task))
        {
            await _hubContext.Clients.Client(connectionId).SendAsync("ReceiveTaskUpdate", task.CurrentStatus);
        }
    }

    // 移除用户连接
    public void RemoveUserConnection(string userId, string connectionId)
    {
        if (_userConnections.TryGetValue(userId, out var connections))
        {
            lock (connections)
            {
                connections.Remove(connectionId);
                if (connections.Count == 0)
                    _userConnections.TryRemove(userId, out _);
            }
        }
    }

    // 给用户发送更新
    public async Task SendUpdateToUser(string userId, object updateData)
    {
        if (_userConnections.TryGetValue(userId, out var connections))
        {
            foreach (var connId in connections)
            {
                await _hubContext.Clients.Client(connId).SendAsync("ReceiveTaskUpdate", updateData);
            }
        }
    }
}

步骤3:实现用户后台任务逻辑

创建 UserBackgroundTask 封装具体的任务逻辑,定期执行并推送更新:

public class UserBackgroundTask
{
    private readonly string _userId;
    private readonly CancellationToken _token;
    private readonly UserTaskManager _taskManager;
    // 当前任务状态,用于用户重新连接时同步
    public object CurrentStatus { get; private set; } = new { Status = "Initializing...", Progress = 0 };

    public UserBackgroundTask(string userId, CancellationToken token, UserTaskManager taskManager)
    {
        _userId = userId;
        _token = token;
        _taskManager = taskManager;
    }

    public async Task RunAsync()
    {
        try
        {
            int progress = 0;
            while (!_token.IsCancellationRequested)
            {
                // 模拟长期运行的任务逻辑(替换成你的业务代码)
                progress = Math.Min(progress + 10, 100);
                CurrentStatus = new { 
                    Status = progress < 100 ? $"Task in progress: {progress}%" : "Task completed!",
                    Progress = progress 
                };

                // 推送更新到用户UI
                await _taskManager.SendUpdateToUser(_userId, CurrentStatus);

                // 模拟任务间隔(根据实际需求调整)
                await Task.Delay(2000, _token);

                if (progress >= 100) break;
            }
        }
        catch (OperationCanceledException)
        {
            CurrentStatus = new { Status = "Task cancelled.", Progress = 0 };
            await _taskManager.SendUpdateToUser(_userId, CurrentStatus);
        }
        finally
        {
            // 任务结束后自动从管理器移除
            _taskManager.StopUserTask(_userId);
        }
    }

    // 取消任务
    public void Cancel()
    {
        (_token as CancellationTokenSource)?.Cancel();
    }
}

步骤4:后端API控制器

创建控制器处理用户的START/STOP请求:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

[ApiController]
[Route("api/[controller]")]
[Authorize] // 确保用户已登录
public class TaskController : ControllerBase
{
    private readonly UserTaskManager _taskManager;

    public TaskController(UserTaskManager taskManager)
    {
        _taskManager = taskManager;
    }

    [HttpPost("Start")]
    public async Task<IActionResult> StartTask()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId)) return Unauthorized();

        await _taskManager.StartUserTask(userId);
        return Ok();
    }

    [HttpPost("Stop")]
    public IActionResult StopTask()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId)) return Unauthorized();

        _taskManager.StopUserTask(userId);
        return Ok();
    }
}

步骤5:前端Razor页面实现

在Razor页面中添加按钮、状态显示区域,以及SignalR客户端逻辑:

@page
@model IndexModel
@{
    ViewData["Title"] = "Long-Running Task Demo";
}

<div class="container mt-5">
    <h1>User-Specific Background Task</h1>
    <div class="mt-3">
        <button id="startBtn" class="btn btn-primary">START</button>
        <button id="stopBtn" class="btn btn-danger ms-2" disabled>STOP</button>
    </div>
    <div id="taskStatus" class="mt-3 p-3 border rounded"></div>
</div>

<!-- 引入SignalR客户端脚本 -->
<script src="~/lib/microsoft/signalr/dist/browser/signalr.js"></script>
<script>
    // 初始化SignalR连接
    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/taskUpdateHub")
        .withAutomaticReconnect() // 断开自动重连
        .build();

    // 接收后端推送的更新
    connection.on("ReceiveTaskUpdate", function(update) {
        const statusEl = document.getElementById("taskStatus");
        statusEl.innerHTML = `
            <p><strong>Status:</strong> ${update.Status}</p>
            <p><strong>Progress:</strong> ${update.Progress}%</p>
        `;

        // 更新按钮状态
        if (update.Status === "Task completed!" || update.Status === "Task cancelled.") {
            document.getElementById("startBtn").disabled = false;
            document.getElementById("stopBtn").disabled = true;
        }
    });

    // 启动SignalR连接
    async function startConnection() {
        try {
            await connection.start();
            console.log("SignalR connected");
        } catch (err) {
            console.error(err);
            setTimeout(startConnection, 5000); // 重试连接
        }
    }
    startConnection();

    // START按钮点击事件
    document.getElementById("startBtn").addEventListener("click", async function() {
        this.disabled = true;
        document.getElementById("stopBtn").disabled = false;

        const response = await fetch("/api/Task/Start", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "RequestVerificationToken": document.querySelector('input[name="__RequestVerificationToken"]').value
            }
        });

        if (!response.ok) {
            alert("Failed to start task");
            this.disabled = false;
            document.getElementById("stopBtn").disabled = true;
        }
    });

    // STOP按钮点击事件
    document.getElementById("stopBtn").addEventListener("click", async function() {
        this.disabled = true;
        document.getElementById("startBtn").disabled = false;

        const response = await fetch("/api/Task/Stop", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "RequestVerificationToken": document.querySelector('input[name="__RequestVerificationToken"]').value
            }
        });

        if (!response.ok) {
            alert("Failed to stop task");
            this.disabled = false;
            document.getElementById("startBtn").disabled = true;
        }
    });

    // 页面关闭时断开连接
    window.addEventListener("beforeunload", () => {
        connection.stop();
    });
</script>

关键特性说明

  1. 用户任务隔离:通过用户ID绑定任务,每个用户的任务独立运行,更新仅推送给该用户的所有连接(支持多设备登录)。
  2. 实时更新:SignalR替代AJAX轮询,后端任务直接推送更新到前端,延迟更低。
  3. 状态续接:用户跳转页面返回后,SignalR自动重连,管理器会同步当前任务状态,用户继续接收后续更新。
  4. 任务生命周期控制:支持手动停止任务,任务完成/取消后自动清理资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:31:01