我有一个.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就可以了
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为空。这与BackgroundService是单例相关,我认为是DI?
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是单例的,我认为这将完成这项工作-因为using
将调用Dispose()
-有时它不这样做await using var scope = _factory.CreateAsyncScope(); var context = scope.ServiceProvider.GetRequiredService<DataContext>(); List<Subject>? currentData = context.Subjects.ToList();
将域添加为服务并将其DI到ConvertService中
一个
现在,我可以在Domains
中DI IServiceScopeFactory,而无需从Convert传递它
1条答案
按热度按时间1l5u6lss1#
问题出在构造函数中:我假定
Domains
对象将由DI创建,但您使用默认构造函数手动创建它:当您使用
new Domains()
呼叫建立Domains
执行严修时,所有成员都将是null
,因为无参数建构函式不会执行任何动作。若要修正这个问题,您必须呼叫
Domains(IServiceScopeFactory)
建构函式。最简单的方法是将
ConverService
中的IServiceScopeFactory
示例存储在字段中,并将其传递给构造函数:然而,请看一下作用域,因为您的代码目前将使您的
DataContext
基本上是一个singelton,这是由于您创建示例的方式。编辑:原创,但错误答案:您不应该在Singleton的建构函式中建立范围。
将作用域视为其创建的服务的所有者。一旦作用域被释放,由该作用域创建的所有可释放服务也将被释放。
在构造函数中创建临时作用域,GC可能会在使用服务之前释放这些作用域。
您应该做的是在构造函数中注入
IServiceScopeFactory
,并根据需要创建和释放服务作用域:有关更多准则,请查看https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines