.NET Core BackgroudService DI Scope null



我有一个。net后台服务

public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly ConvertService _convert;
public Worker(ILogger<Worker> logger, ConvertService convert)
{
_logger = logger;
_convert = convert;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _convert.GetXML();
await Task.Delay(TimeSpan.FromHours(12), stoppingToken);
}
}
}

Program.cs

using Microsoft.EntityFrameworkCore;
using P1_APT_SUB;
using P1_APT_SUB.BusService;
using P1_APT_SUB.Persistence;

IHost host = Host.CreateDefaultBuilder(args)

.UseWindowsService(options =>
{
options.ServiceName = "APT";
})
.ConfigureServices((context,services) =>
{
services.AddHttpClient("auth", httpClient =>
{


});
services.AddHttpClient("ODM", httpClient =>
{

});
services.AddTransient<ConvertService>();
services.AddTransient<BusServiceClass>();
services.AddHostedService<Worker>();
services.AddDbContext<DataContext>(
options =>
{
options.UseSqlServer();
});
})
.Build();
await host.RunAsync();

ConvertService.cs

这里是创建作用域并将其传递给方法,如GetST()和httpCLient是ok

using Newtonsoft.Json.Linq;
using System.Xml.Linq;
using P1_APT_SUB.BusService;
using P1_APT_SUB.Persistence;
using P1_APT_SUB.Entities;
namespace P1_APT_SUB
{

public class ConvertService
{
private readonly IHttpClientFactory? _httpClientFactory;
private readonly IConfiguration? _config;
private readonly DataContext _dataContext;

private readonly BusServiceClass _busService;
static public string? sites;

public ConvertService(IConfiguration config, IServiceScopeFactory factory)
{
_httpClientFactory = factory.CreateScope().ServiceProvider.GetRequiredService<IHttpClientFactory>();
_config = config;
_dataContext = factory.CreateScope().ServiceProvider.GetRequiredService<DataContext>();
_busService = factory.CreateScope().ServiceProvider.GetRequiredService<BusServiceClass>();
}
public ConvertService()
{
}

public async Task GetXML()
{
var data = await new Domains().GetST(_httpClientFactory, _config);
sites = await new Domains().GetSD(_httpClientFactory, _config);

}
}}
}

Domains.cs

using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace P1_APT_SUB
{
public class Domains
{
public string subjects;
public string sites;
private readonly IHttpClientFactory _httpClientFactory;

public async Task<string> GetST(IHttpClientFactory _httpClientFactory, IConfiguration _config)
{

var accessToken = await Authorization.GetAccessToken(_httpClientFactory);
var httpClient = _httpClientFactory.CreateClient("ODM");
httpClient.DefaultRequestHeaders.Add("X-ACCESS-TOKEN", accessToken);
var httpResponseMessage = await httpClient.GetAsync();
if (httpResponseMessage.IsSuccessStatusCode)
{
var contentStream =
await httpResponseMessage.Content.ReadAsStringAsync();
subjects = contentStream;

}
return subjects;



}
public async Task<string> GetSD(IHttpClientFactory _httpClientFactory, IConfiguration _config)
{


var accessToken = await Authorization.GetAccessToken(_httpClientFactory);
var httpClient = _httpClientFactory.CreateClient("ODM");
httpClient.DefaultRequestHeaders.Add("X-ACCESS-TOKEN", accessToken);
var httpResponseMessage = await httpClient.GetAsync();
if (httpResponseMessage.IsSuccessStatusCode)
{
var contentssStream =
await httpResponseMessage.Content.ReadAsStringAsync();
sites = contentssStream;
}
return sites;


}

}
}

但是当我想直接在Domains中DI/创建范围时,就像下面的httpCLient是空的。这与后台服务是单例的有关,我认为是这样吗?

using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace P1_APT_SUB
{
public class Domains
{
public string subjects;

private readonly IHttpClientFactory _httpClientFactory;
public Domains(IServiceScopeFactory factory)
{
this.subjects = subjects;
this.sites = sites;
_httpClientFactory = factory.CreateScope().ServiceProvider.GetRequiredService<IHttpClientFactory>();
}
public Domains()
{
}
public async Task<string> GetSubjects(IConfiguration _config)
{

var accessToken = await Authorization.GetAccessToken(**_httpClientFactory**);
var httpClient = _httpClientFactory.CreateClient("ODM");
httpClient.DefaultRequestHeaders.Add("X-ACCESS-TOKEN", accessToken);
var httpResponseMessage = await httpClient.GetAsync();
if (httpResponseMessage.IsSuccessStatusCode)
{
var contentStream =
await httpResponseMessage.Content.ReadAsStringAsync();
subjects = contentStream;

}
return subjects;



}


}
}

编辑

关于DataContext是单例的,我认为这将做的工作?就像usingDispose()的称呼一样?有时不

await using var scope = _factory.CreateAsyncScope(); var context = scope.ServiceProvider.GetRequiredService<DataContext>(); List<Subject>? currentData = context.Subjects.ToList();


将domain作为服务添加到ConvertService中

services.AddTransient<Domains>();

public ConvertService(IConfiguration config, IServiceScopeFactory factory, Domains domains)
{
_httpClientFactory = factory.CreateScope().ServiceProvider.GetRequiredService<IHttpClientFactory>();
_config = config;
_domains = domains;
_factory = factory;

_busService = factory.CreateScope().ServiceProvider.GetRequiredService<BusServiceClass>();
}

现在我可以在Domains中DI IServiceScopeFactory而不需要从Convert

传递它

问题是你的构造函数:我假设Domains对象将由DI创建,但你通过使用默认构造函数手动创建它:

public Domains(IServiceScopeFactory factory)
{
this.subjects = subjects;
this.sites = sites;
_httpClientFactory = factory.CreateScope().ServiceProvider.GetRequiredService<IHttpClientFactory>();
}
public Domains()
{
}

当你用new Domains()调用createDomains实例时,所有成员都将是null,因为无参数构造函数不做任何事情。

要解决这个问题,必须调用Domains(IServiceScopeFactory)构造函数。

最简单的方法是将ConverService中的IServiceScopeFactory实例存储在一个字段中,并将其传递给构造函数:

var data = await new Domains(_factory).GetST(_httpClientFactory, _config);
sites = await new Domains(_factory).GetSD(_httpClientFactory, _config);

然而,请看看作用域,因为你的代码目前将使你的DataContext基本上是一个单例,因为你创建实例的方式。

编辑:原来的,但错误的答案:不应该在单例的构造函数中创建作用域。

将作用域视为其创建的服务的所有者。一旦范围由作用域创建的所有一次性服务也将被丢弃。

在构造函数中创建临时作用域,这些作用域可能在使用服务之前被GC处理。

你应该做的是在你的构造函数中注入IServiceScopeFactory,并根据需要创建和处置你的服务范围:

// Create new scope and dispose after use
using var scope = _serviceScopeFactory.CreateScope();
// Resolve instances
var service = scope.ServiceProvider.GetRequiredService<TService>();

有关更多指南,请参阅https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines

最新更新