C# 基于Microsoft.AspNetCore.Owin的Web API如何存储客户端任务运行状态
实现方案
1 不要直接在Controller中存储状态
ASP.NET Core中控制器默认是瞬态(Transient)生命周期,每次请求都会创建新的控制器实例,你在控制器内声明的普通字段/属性会在每次请求结束后销毁,无法跨请求共享状态。如果声明为静态字段虽然可以实现共享,但会导致代码耦合度极高、无法单元测试、并发控制逻辑难以维护,不推荐使用。
2 推荐方案:注入单例并发状态服务
你可以自定义一个线程安全的状态管理类,注册为全局单例,再注入到控制器中使用,步骤如下:
2.1 定义状态实体和状态管理服务
首先定义任务状态的实体类,再封装基于ConcurrentDictionary(.NET 自带的线程安全字典)的状态管理逻辑:
// 任务状态实体 public class WorkerTaskState { public string TaskId { get; set; } public DateTime AssignTime { get; set; } public bool IsCompleted { get; set; } } // 状态管理服务接口,方便后续单元测试替换实现 public interface IWorkerTaskStateService { // 获取指定worker的任务状态 WorkerTaskState GetWorkerState(string workerName); // 给worker分配新任务,返回新任务 WorkerTaskState AssignNewTask(string workerName); } // 内存实现的状态管理服务 public class MemoryWorkerTaskStateService : IWorkerTaskStateService { // 线程安全字典,key为workerName,value为对应的任务状态 private readonly ConcurrentDictionary<string, WorkerTaskState> _workerStates = new(); public WorkerTaskState GetWorkerState(string workerName) { _workerStates.TryGetValue(workerName, out var state); return state; } public WorkerTaskState AssignNewTask(string workerName) { var newState = new WorkerTaskState { TaskId = Guid.NewGuid().ToString("N"), AssignTime = DateTime.Now, IsCompleted = false }; // AddOrUpdate是原子操作,存在则更新,不存在则添加 _workerStates.AddOrUpdate(workerName, newState, (key, old) => newState); return newState; } }
2.2 注册服务为全局单例
在你的启动类中注册服务,确保整个应用生命周期内只有一个服务实例,状态可以跨请求保持:
- 如果是Startup.cs的写法,在
ConfigureServices方法中添加:
services.AddSingleton<IWorkerTaskStateService, MemoryWorkerTaskStateService>();
- 如果是.NET 6+的顶层语句Program.cs写法,直接在builder阶段添加:
builder.Services.AddSingleton<IWorkerTaskStateService, MemoryWorkerTaskStateService>();
2.3 在控制器中注入使用
修改你的TaskController,注入状态服务即可直接在接口中调用:
[ApiController] [Route("[controller]")] public class TaskController : ControllerBase { private readonly IWorkerTaskStateService _stateService; // 构造函数注入服务 public TaskController(IWorkerTaskStateService stateService) { _stateService = stateService; } [HttpGet] public IActionResult Get(string workerName) { if (string.IsNullOrWhiteSpace(workerName)) return Ok("Empty"); var existState = _stateService.GetWorkerState(workerName); // 该worker没有分配过任务,或者上一个任务已完成,可以分配新任务 if (existState == null || existState.IsCompleted) { var newTask = _stateService.AssignNewTask(workerName); return Ok(new { TaskId = newTask.TaskId, Msg = "新任务分配成功" }); } // 还有未完成的任务,返回现有任务 return Ok(new { TaskId = existState.TaskId, Msg = "已有进行中的任务" }); } }
3 注意事项
- 内存存储的状态会在服务重启、进程退出时丢失,如果需要持久化状态,可以将服务的实现替换为数据库存储即可,上层业务代码不需要修改。
- 如果你有多个连续的状态操作需要保证原子性,可以在服务内部加锁处理,避免并发冲突。
- 你可以根据业务需求扩展
WorkerTaskState的字段,比如存储任务内容、超时时间等。
内容的提问来源于stack exchange,提问作者VansFannel
相关产品推荐
相关产品推荐

