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

.NET 6 BackgroundService注入EF Core仓储空引用异常解决方法

问题根源
  • 你在Program.cs里创建了两个完全独立的宿主:第一个单独用Host.CreateDefaultBuilder建的宿主只注册了APIService,根本没注册DataContext、IAppRepository这些依赖,而且你先调用了await host.RunAsync()这个阻塞方法,后面写的Web应用相关的服务注册、中间件配置代码根本不会执行,完全是无效代码。
  • APIService写了两个构造函数,依赖注入容器只会选择一个它能满足所有参数的构造函数执行。在第一个独立宿主的容器里只有ILogger<APIService>能被解析,所以容器只会调用第一个只接收Logger参数的构造函数,_appRepository从来没被赋值,当然是null。
  • 生命周期不匹配:BackgroundService是单例生命周期,而你注册的DbContext和IAppRepository都是Scoped生命周期,单例服务不能直接注入Scoped服务,就算前面两个问题修复,这里也会抛出 captive dependency 异常,还会导致DbContext被长期持有不释放、并发访问报错等问题。
修复方案

第一步:合并Program.cs里的两个宿主,所有服务注册到同一个容器

删掉你单独创建的Generic Host代码,把后台服务、DbContext、仓储都注册到WebApplication的服务集合里,只保留一个WebApplication实例运行。
正确的Program.cs代码:

var builder = WebApplication.CreateBuilder(args);

// 注册MVC控制器服务
builder.Services.AddControllersWithViews();
// 注册EF Core DbContext
builder.Services.AddDbContext<DataContext>(x =>
    x.UseSqlServer(builder.Configuration.GetConnectionString("BankSearchConnection")));
// 注册仓储
builder.Services.AddScoped<IAppRepository, AppRepository>();
// 把后台服务注册到当前服务容器
builder.Services.AddHostedService<APIService>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");

// 只需要运行这一个应用即可
app.Run();

第二步:修改APIService,合并构造函数,通过作用域工厂获取Scoped服务

删掉重复的构造函数,不要直接注入IAppRepository,改为注入IServiceScopeFactory,每次需要操作数据库的时候创建一个作用域,从作用域中获取仓储实例,用完自动释放。
修改后的APIService.cs代码:

public class APIService : BackgroundService
{
    private readonly ILogger<APIService> _logger;
    private readonly IServiceScopeFactory _scopeFactory;

    // 只保留一个构造函数,声明所有需要的依赖
    public APIService(ILogger<APIService> logger, IServiceScopeFactory scopeFactory)
    {
        _logger = logger;
        _scopeFactory = scopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Banka verileri güncellendi! Saat: {time}", DateTimeOffset.Now);
            BankAPI();
            _logger.LogInformation("Garanti Bankası geçti");
            await Task.Delay(180000, stoppingToken);
        }
    }

    private void BankAPI()
    {
        string uri = "https://apis.garantibbva.com.tr:443/loans/v1/paymentPlan";
        var client = new RestClient(uri);
        var request = new RestRequest(Method.POST);
        request.AddHeader("apikey", "l7xx8af86c14ea7e44e0ab3fbfcc6137ae09");
        request.AddQueryParameter("loanType", "1");
        request.AddQueryParameter("campaignCode", "BankSearch");
        request.AddQueryParameter("loanAmount", "10000");
        IRestResponse response = client.Execute(request);
        
        int dueNum = 3;
        float monthlyAnnualCostRate = JObject.Parse(response.Content)["data"]["list"]
            .Where(p => (int)p["dueNum"] == dueNum)
            .Select(p => (float)p["monthlyAnnualCostRate"])
            .FirstOrDefault();

        _logger.LogInformation("Garanti Bankası Response: {monthlyAnnualCostRate}", monthlyAnnualCostRate);

        AddKredi("Garanti Bankası",1,monthlyAnnualCostRate);
    }

    private void AddKredi(string bankaAdi, int krediVadeKodu, float krediFaizi)
    {
        Kredi kredi = new Kredi(bankaAdi, krediVadeKodu, krediFaizi);
        // 创建作用域解析Scoped服务,用完自动释放
        using var scope = _scopeFactory.CreateScope();
        var appRepository = scope.ServiceProvider.GetRequiredService<IAppRepository>();
        appRepository.Add(kredi);
        appRepository.SaveAll();
    }
}

IAppRepository.cs和AppRepository.cs的代码不需要修改。
改完之后就能正常注入,不会出现空引用,也符合EF Core在后台服务中使用的生命周期规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:09:10