.NET核心背景服务DI范围为空

9lowa7mx  于 2022-12-01  发布在  .NET
关注(0)|答案(1)|浏览(126)

我有一个.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传递它

1l5u6lss

1l5u6lss1#

问题出在构造函数中:我假定Domains对象将由DI创建,但您使用默认构造函数手动创建它:

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

public Domains()
{
}

当您使用new Domains()呼叫建立Domains执行严修时,所有成员都将是null,因为无参数建构函式不会执行任何动作。
若要修正这个问题,您必须呼叫Domains(IServiceScopeFactory)建构函式。
最简单的方法是将ConverService中的IServiceScopeFactory示例存储在字段中,并将其传递给构造函数:

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

然而,请看一下作用域,因为您的代码目前将使您的DataContext基本上是一个singelton,这是由于您创建示例的方式。
编辑:原创,但错误答案:您不应该在Singleton的建构函式中建立范围。
将作用域视为其创建的服务的所有者。一旦作用域被释放,由该作用域创建的所有可释放服务也将被释放。
在构造函数中创建临时作用域,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

相关问题