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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 06:51:02