一些服务无法构建(在验证服务描述符“ServiceType:IHostedService”时发生错误)。

12

我正在使用.NET 5,想要运行一个后台任务,使用IHostedService类,如下所示:

public class PasargadJobs : IHostedService, IDisposable
{
    private Timer _timer = null!;
    readonly  ITerminalService _terminalService;
    readonly  IMessageService _messageService;
    readonly IMerchantService _merchantService;
    
    public PasargadJobs(
        IMerchantService merchantService,
        ITerminalService terminalService,
        IMessageService messageService)
    {
        _messageService = messageService;
        _terminalService = terminalService;
        _merchantService = merchantService;
    }
    
    //other parts of code are removed for short code
}

所以我必须将它注入到服务集合中,正如您所看到的:
services.AddHostedService<PasargadJobs>();

但是在添加这个后,我遇到了以下错误:
System.AggregateException:“无法构造某些服务(验证服务描述符时出错'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: MIMS.Portal.ScheduleJobs.PasargadJobs':无法从单例'Microsoft.Extensions.Hosting.IHostedService'中使用范围服务 'Microsoft.EntityFrameworkCore.DbContextOptions`1[MIMS.Portal.Infrustructure.Repositories.AppDbContext]'。”)
这是我的startup.cs文件:
services.AddExpressiveAnnotations();

//--- DB Context ---//
services.AddTransient<AppDbContext, AppDbContext>();
//--- Repositories ---//
services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddTransient<IMerchantRepository, MerchantRepository>();
services.AddTransient<IBankAccountRepository, BankAccountRepository>();
services.AddTransient<IBankRepository, BankRepository>();
services.AddTransient<IGeoRepository, GeoRepository>();
services.AddTransient<IDepartmentRepository, DepartmentRepository>();
services.AddTransient<IDeviceRepository, DeviceRepository>();
services.AddTransient<IInvoiceRepository, InvoiceRepository>();
services.AddTransient<IStoreRepository, StoreRepository>();
services.AddTransient<ITerminalRepository, TerminalRepository>();
services.AddTransient<ITicketRepository, TicketRepository>();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<IFileRepository, FileRepository>();
services.AddTransient<IPartnerRepository, PartnerRepository>();
services.AddTransient<IStoreScopeRepository, StoreScopeRepository>();
services.AddTransient<ICommentRepository, CommentRepository>();
services.AddTransient<INewsRepository, NewsRepository>();
services.AddTransient<IPosBrandRepository, PosBrandRepository>();
//-- Services --//
services.AddTransient<IMerchantService, MerchantService>();
services.AddTransient<IBankAccountService, BankAccountService>();
services.AddTransient<IBankService, BankService>();
services.AddTransient<IGeoService, GeoService>();
services.AddTransient<IDepartmentService, DepartmentService>();
services.AddTransient<IDeviceService, DeviceService>();
services.AddTransient<IInvoiceService, InvoiceService>();
services.AddTransient<IStoreService, StoreService>();
services.AddTransient<ITerminalService, TerminalService>();
services.AddTransient<ITicketService, TicketService>();
services.AddTransient<IUsersService, UsersService>();
services.AddTransient<IFilesManagerService, FilesManagerService>();
services.AddTransient<IMessageService, MessageService>();
services.AddTransient<IPartnerService, PartnerService>();
services.AddTransient<IStoreScopeService, StoreScopeService>();
services.AddTransient<INewsService, NewsService>();
services.AddTransient<ICommentService, CommentService>();
services.AddTransient<IPosBrandService, PosBrandService>();

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(option =>
    {
        option.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "localhost",
            ValidAudience = "localhost",
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes("wevhgfrtyhasdfghjklzxcvbnm"))
        };
    });
    
services.AddHostedService<PasargadJobs>();
2个回答

20
您的托管服务是单例的,并且您对作用域服务有间接依赖: 无法从单例 'Microsoft.Extensions.Hosting.IHostedService' 中使用作用域服务 'Microsoft.EntityFrameworkCore.DbContextOptions`1[MIMS.Portal.Infrustructure.Repositories.AppDbContext]' 您可以通过以下方式解决问题:
IServiceScopeFactory 注入到您的托管服务中,并手动创建服务作用域,如下所示。
using var scope = _serviceScopeFactory.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IScopedService>();

不要将 CreateScopeGetRequiredService 放在托管服务的构造函数中:由于托管服务是单例的,构造函数只会被调用一次,因此通过 scope.ServiceProvider.GetRequiredService<T>() 创建的实例也只会被创建一次。因此,在这种情况下,您将使用设计为短暂生命周期的服务作为永久性单例。此外,在使用后不要忘记处理 scope
由于看起来您正在使用 Entity Framework Core,您还可以考虑重构您的服务以使用 IDbContextFactory<TContext> 作为替代方法,以便可以将它们注册为单例服务。

谢谢。我已经更改了类:readonly IServiceScopeFactory _serviceScopeFactory; public PasargadJobs(IServiceScopeFactory serviceScopeFactory) {_serviceScopeFactory = serviceScopeFactory; var scope = _serviceScopeFactory.CreateScope(); var _terminalService = scope.ServiceProvider.GetRequiredService(); var _merchantService = scope.ServiceProvider.GetRequiredService(); var _messageService = scope.ServiceProvider.GetRequiredService();} - Ehsan Akbar
但是 terminalService、merchandService 和 messageService 为什么为空? - Ehsan Akbar
我可以发现两个问题:首先,看起来你正在将服务分配给本地变量而不是字段:var _terminalService = ... 而不是 _terminalService = ... 这样你会将服务分配给本地变量,而字段 _terminalService 仍然为 null。另外不要在构造函数中创建服务,这基本上会使所有服务成为单例。 - Christian Held
1
谢谢,它起作用了。我把它们从构造函数移到了我的方法中,现在它可以工作了。 - Ehsan Akbar
我真的需要处理 scope 吗?你正在使用 using,它会在作用域结束时自动为我处理。我有什么遗漏吗? - Konrad Viltersten
确保及时释放作用域中的资源非常重要,因此需要使用适当的方式进行处理。 - Christian Held

-1
在你的类中实现一个BackgroundService。它已经实现了IHostedServiceIDisposable
public class PasargadJobs : BackgroundService

services.AddHostedService<PasargadJobs>();

1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接