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

时间:2019-03-29 08:53:26

标签: 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具有Singleton生存期。是否有可能以某种方式消耗范围为UnitOfWork的MailService依赖项?

我了解IServiceScopeFactory,但也许不知道如何正确使用它。

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

ScopedMailService:

imageView.SetBackgroundColor(Android.Graphics.Color.Transparent);

ConsumeScopedMailService:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
    android:src="@drawable/successful"
    android:layout_width="250dp"
    android:layout_gravity="center"
    android:background="@android:color/transparent"
    android:layout_height="140dp"
    android:id="@+id/imageView1" />

Startup.cs:

public class ScopedMailService : IScopedMailService
{
    private readonly IMailService mailService;

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

    public void DoWork()
    {
        mailService.SendNewsletterToSubscribedUsers();
    }
}

MailService:

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;
    }
}

1 个答案:

答案 0 :(得分:2)

单例ConsumeScopedMailService通过其构造函数依赖于IMailService

public ConsumeScopedMailService(IServiceProvider services, IMailService mailService)

IMailService可能是临时的,但实现它的类取决于作用域服务IUnitOfWork。间接地,ConsumeScopedMailService最终取决于作用域服务。

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