如何在后台任务中使用具有依赖性的范围服务

Sad*_*nus 8 c# dependency-injection background-process background-service asp.net-core-2.2

这是我的风景。我想使用后台任务向订阅用户发送时事通讯。这是由 MailService 完成的,它具有 UnitOfWork 作为依赖项。

我尝试了docs.microsoft.com 中的解决方案, 因此在我的情况下,我使用 IMailService 方法而不是 ILogger,但出现错误:

System.InvalidOperationException: '不能从单例 >'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor' 使用范围服务 >'Fit4You.Core.Data.IUnitOfWork'。

我不想让我的 UnitOfWork 或 DbContext 具有单例生命周期。是否有可能以某种方式使用范围为 UnitOfWork 的 MailService 的依赖项?

我知道 IServiceScopeFactory 但可能不知道如何正确使用它。

我正在使用 .NET Core 2.2 并内置接口 IHostedService

范围邮件服务:

public class ScopedMailService : IScopedMailService
{
    private readonly IMailService mailService;

    public ScopedMailService(IMailService mailService)
    {
        this.mailService = mailService;
    }

    public void DoWork()
    {
        mailService.SendNewsletterToSubscribedUsers();
    }
}
Run Code Online (Sandbox Code Playgroud)

消费范围邮件服务:

public class ConsumeScopedMailService : IHostedService
{
    private Timer timer;
    private readonly IMailService mailService;
    public IServiceProvider Services { get; }

    public ConsumeScopedMailService(IServiceProvider services, IMailService mailService)
    {
        Services = services;
        this.mailService = mailService;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        var startTimeSpan = GetStartTimeSpan();
        var periodTimeSpan = TimeSpan.FromSeconds(30);

        timer = new Timer(DoWork, null, startTimeSpan, periodTimeSpan);

        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        using (var scope = Services.CreateScope())
        {
            var scopedMailService = scope.ServiceProvider.GetRequiredService<IScopedMailService>();
            scopedMailService.DoWork();
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        timer?.Dispose();
    }

    private TimeSpan GetStartTimeSpan()
    {
        var currentTime = DateTime.Now.Ticks;
        var executeTime = DateTime.Today.AddHours(8)
                                        .AddMinutes(0)
                                        .Ticks;

        long ticks = executeTime - currentTime;

        if (ticks < 0)
        {
            ticks = ticks + TimeSpan.TicksPerDay;
        }

        var startTimeSpan = new TimeSpan(ticks);

        return startTimeSpan;
    }
}
Run Code Online (Sandbox Code Playgroud)

启动.cs:

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddDbContext<Fit4YouDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("MyConnectionString")));

    services.AddScoped<IUnitOfWork, UnitOfWork>();
    services.AddTransient<IMailService, MailService>();

    services.AddHostedService<ConsumeScopedMailService>();
    services.AddScoped<IScopedMailService, ScopedMailService>();

    ...
}
Run Code Online (Sandbox Code Playgroud)

邮件服务:

    public class MailService : IMailService
    {
        private readonly IUnitOfWork unitOfWork;
        public MailService(IUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
        }

        public void SendNewsletterToSubscribedUsers()
        {
            // Some Code
        }
    }
Run Code Online (Sandbox Code Playgroud)

Pan*_*vos 4

单例ConsumeScopedMailService依赖于IMailService它的构造函数:

public ConsumeScopedMailService(IServiceProvider services, IMailService mailService)
Run Code Online (Sandbox Code Playgroud)

IMailService可能是瞬态的,但实现它的类依赖于作用域服务,IUnitOfWork. 间接地,ConsumeScopedMailService最终取决于范围服务。

要解决此问题,IMailService mailService应将其删除。无论如何,它没有在发布的代码中使用。