C# Web应用Razor Pages多摄像头连接静态对象复用实现方法
实现方案
你可以直接基于ASP.NET Core原生的依赖注入(DI)体系实现多摄像头单例复用,比自定义多单例模式更易维护,也天然适配Web API和Razor Pages的调用逻辑。
1. 定义摄像头服务基础结构
首先封装摄像头的操作接口和实现,把你之前单例里的连接、抓图逻辑迁移进去:
// 摄像头操作接口 public interface ICameraService { // 摄像头唯一标识,可使用设备IP、序列号等作为唯一ID string CameraId { get; } // 抓取图像方法 byte[] CaptureImage(); // 连接/释放方法 void Connect(); void Disconnect(); } // 摄像头配置映射类,对应appsettings.json里的配置 public class CameraConfig { public string CameraId { get; set; } public string ConnectionString { get; set; } } // 摄像头服务实现类 public class CameraService : ICameraService, IDisposable { public string CameraId { get; private set; } private readonly string _connectionString; // 这里存放你原有单例中的摄像头私有字段,比如SDK实例、连接句柄、锁对象等 private readonly object _lockObj = new object(); private bool _disposed = false; public CameraService(string cameraId, string connectionString) { CameraId = cameraId; _connectionString = connectionString; // 构造函数内执行初始化连接 Connect(); } public void Connect() { // 你原有单例里的摄像头连接逻辑 } public byte[] CaptureImage() { // 注意加锁保证线程安全,避免多请求同时调用SDK冲突 lock (_lockObj) { // 你原有单例里的抓图逻辑 throw new NotImplementedException(); } } public void Disconnect() { // 摄像头连接释放逻辑 } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { Disconnect(); } _disposed = true; } }
2. 注册多摄像头单例实例
在Program.cs中把5个摄像头实例注册为全局单例,同时注册工厂类方便按ID快速获取对应摄像头:
// 从配置文件读取5个摄像头的配置,appsettings.json配置示例: // "CameraSettings": [ // { "CameraId": "cam01", "ConnectionString": "rtsp://192.168.1.101/stream" }, // { "CameraId": "cam02", "ConnectionString": "rtsp://192.168.1.102/stream" }, // { "CameraId": "cam03", "ConnectionString": "rtsp://192.168.1.103/stream" }, // { "CameraId": "cam04", "ConnectionString": "rtsp://192.168.1.104/stream" }, // { "CameraId": "cam05", "ConnectionString": "rtsp://192.168.1.105/stream" } // ] var cameraConfigs = builder.Configuration.GetSection("CameraSettings").Get<List<CameraConfig>>(); // 批量注册5个摄像头单例实例,应用启动时只会初始化一次 foreach (var config in cameraConfigs) { builder.Services.AddSingleton<ICameraService>(sp => new CameraService(config.CameraId, config.ConnectionString)); } // 注册摄像头工厂,方便按ID查询实例 builder.Services.AddSingleton<ICameraFactory, CameraFactory>();
工厂类实现代码:
public interface ICameraFactory { ICameraService GetCameraById(string cameraId); IEnumerable<ICameraService> GetAllCameras(); } public class CameraFactory : ICameraFactory { private readonly IEnumerable<ICameraService> _allCameras; public CameraFactory(IEnumerable<ICameraService> allCameras) { _allCameras = allCameras; } public ICameraService GetCameraById(string cameraId) { return _allCameras.FirstOrDefault(c => c.CameraId == cameraId) ?? throw new KeyNotFoundException($"不存在ID为{cameraId}的摄像头"); } public IEnumerable<ICameraService> GetAllCameras() => _allCameras; }
3. 业务层调用
不管是Web API的Controller还是Razor Pages的PageModel,直接注入工厂类即可调用,所有摄像头实例都是全局复用的,不会随请求重复创建:
// Web API调用示例 [ApiController] [Route("api/camera")] public class CameraController : ControllerBase { private readonly ICameraFactory _cameraFactory; // 构造函数注入工厂 public CameraController(ICameraFactory cameraFactory) { _cameraFactory = cameraFactory; } [HttpGet("capture/{cameraId}")] public IActionResult CaptureImage(string cameraId) { var camera = _cameraFactory.GetCameraById(cameraId); var imageBytes = camera.CaptureImage(); return File(imageBytes, "image/jpeg"); } }
注意事项
- 摄像头服务的公共方法必须保证线程安全,除了加锁之外,也可以用并发队列串行处理抓图请求,避免SDK并发调用异常
- 实现
IDisposable接口后,DI容器会在应用停止时自动调用释放逻辑,不用手动处理摄像头断连 - 如果需要断连自动重连,直接在
CameraService内部加心跳检测和重连逻辑即可,上层调用无需修改
内容的提问来源于stack exchange,提问作者Anestis Si
相关产品推荐
相关产品推荐

