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

应用启动时仅调用一次CarService获取数据并全局复用的实现方案问询

解决方案

要实现应用启动时仅调用一次CarService.GetData(),并将结果通过DI共享给所有消费者,可以通过两种方案实现,以下是具体实现细节:


方案1:改造CarService实现内部缓存(懒加载)

让单例的CarService在首次调用GetData()时加载数据并缓存,后续调用直接返回缓存结果,避免重复请求。这种方式无需额外HostedService,实现简单。

修改后的CarService代码

public interface ICarService
{
    Task<List<Car>> GetData();
}

public class CarService : ICarService
{
    private readonly IHttpClientFactory _httpClientFactory;
    private List<Car> _cachedCars;
    // 用信号量防止并发场景下重复发起请求
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

    public CarService(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<List<Car>> GetData()
    {
        // 已缓存则直接返回
        if (_cachedCars != null)
            return _cachedCars;

        await _semaphore.WaitAsync();
        try
        {
            // 双重检查,避免等待信号量期间数据已被其他线程加载
            if (_cachedCars != null)
                return _cachedCars;

            var client = _httpClientFactory.CreateClient();
            var req = new HttpRequestMessage(HttpMethod.Get, "https://localhost:5011/cars");        
            var res = await client.SendAsync(req);
            res.EnsureSuccessStatusCode(); // 确保请求成功
            // 假设使用System.Text.Json序列化,可根据实际调整
            var data = await res.Content.ReadFromJsonAsync<List<Car>>(); 
            _cachedCars = data ?? new List<Car>();
            return _cachedCars;
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

Program.cs注册

// 保持单例注册即可,无需HostedService
services.AddSingleton<ICarService, CarService>();

方案2:启动时预加载数据(主动加载)

通过IHostedService在应用启动阶段主动调用GetData(),将结果注册为单例,后续消费者直接注入缓存数据或封装后的服务。这种方式确保数据在应用启动完成后就已准备好,无首次请求延迟。

步骤1:定义缓存数据服务

// 封装缓存数据的服务,方便消费者注入
public interface ICachedCarService
{
    List<Car> GetCachedCars();
}

public class CachedCarService : ICachedCarService
{
    private readonly List<Car> _cars;

    public CachedCarService(List<Car> cars)
    {
        _cars = cars;
    }

    public List<Car> GetCachedCars()
    {
        return _cars;
    }
}

步骤2:实现预加载HostedService

public class PreloadCarsHostedService : IHostedService
{
    private readonly IServiceProvider _serviceProvider;

    public PreloadCarsHostedService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        using var scope = _serviceProvider.CreateScope();
        var carService = scope.ServiceProvider.GetRequiredService<ICarService>();
        var cars = await carService.GetData();

        // 将加载好的汽车列表注册为单例
        var serviceCollection = _serviceProvider.GetRequiredService<IServiceCollection>();
        serviceCollection.AddSingleton(cars);
        serviceCollection.AddSingleton<ICachedCarService, CachedCarService>();
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

步骤3:Program.cs注册

services.AddSingleton<ICarService, CarService>();
services.AddHostedService<PreloadCarsHostedService>();

消费者使用示例

public class CarsController : ControllerBase
{
    private readonly ICachedCarService _cachedCarService;

    public CarsController(ICachedCarService cachedCarService)
    {
        _cachedCarService = cachedCarService;
    }

    [HttpGet]
    public IActionResult GetCars()
    {
        var cars = _cachedCarService.GetCachedCars();
        return Ok(cars);
    }
}

方案对比

方案优点缺点
方案1(懒加载)代码简洁,无需额外服务首次调用GetData()会有请求延迟
方案2(预加载)数据在启动阶段完成加载,无首次请求延迟需要额外定义服务,代码稍复杂

可根据实际场景选择合适的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 17:53:13