如何在后台任务中解析服务?

问题描述 投票:0回答:2

我已经按照here的说明实现了BackgroundQueue。这将替代旧的HostingEnvironment。此外,我正在重构代码以使用Autofac将服务注入后台任务。

当前,代码是这样的:

public ActionResult SomeAction()
{
    backgroundQueue.QueueBackgroundWorkItem(async ct =>
    {
        //Need to resolve services here...
    }

    return Ok();
}

[backgroundQueueIBackgroundQueue的实例,并作为单例向Autofac注册。

如何将Autofac容器传递给任务,以便我可以注册服务?还是有更好的方法来注册任务中的服务?

解决方案可能正在这样做:

var myService = HttpContext.RequestServices.GetService(typeof(IMyService));

但是这被认为是反专利。

c# asp.net-core autofac
2个回答
0
投票

您将必须在任务中管理自己的LifetimeScope

最简单的方法是更改​​方法QueueBackgroundWorkItem以引入ILifetimeScope

public interface IBackgroundTaskQueue
{
    void QueueBackgroundWorkItem(Func<ILifetimeScope, CancellationToken, Task> workItem);

然后

public ActionResult SomeAction()
{
    backgroundQueue.QueueBackgroundWorkItem(async (scope, ct) =>
    {
        scope.Resolve<IService>().Do()
        //Need to resolve services here...
    }

    return Ok();
}

您可以使用现有示波器的ILifetimeScope获得新的BeginLifetimeScope,并且ILifetimeScope是已注册的服务。

如果您使用链接提供的QueueHostedService实现,则可以如下进行更改

public class QueueHostedService: IBackgroundTaskQueue {
    public QueueHostedService(ILifetimeScope scope, ...) {
        this._rootScope = scope; 
    }

    private readonly ILifetimeScope _rootScope; 

    ...

     private async Task BackgroundProcessing(...) {
        ... 
        try {
            using(ILifetimeScope queueScope = this._rootScope.BeginLifetimeScope()){
                await workItem(queueScope, stoppingToken);
            }
        }
        ...
     }

如果无法更改方法定义,则可以在任务内创建生命周期范围。您可以在控制器内部注入ILifetimeScope,但不能从中创建LifetimeScope,因为它将在请求结束时处理。您可以解析一个命名的lifetimescope,它将成为所有队列生存期作用域的根

public class XController {

  public XController(ILifetimeScope scope){
      // you can also inject directly the named scope using named attribute or custom parameter, etc. 
      this._taskRootScope.ResolveNamed<ILifetimeScope>("taskRoot"); 
  }
  private readonly ILifetimeScope _taskRootScope; 


  public ActionResult SomeAction()
  {
    var taskRootScope = this._taskRootScope;
    backgroundQueue.QueueBackgroundWorkItem(async ct =>
    {
      using(var taskScope = taskRootScope.BeginLifetimeScope()){
          taskScope.Resolve<IService>().Do();
      }
    }

    return Ok();
  }
}

注册将类似于

builder.Register(c => c.Resolve<ILifetimeScope>())
       .Named<ILifetimeScope>("taskRoot")
       .SingleInstance(); 

还有许多其他方法可以自己处理范围。

下一步可能是使用方法参数注入,如ASP.net核心所做的那样,结果将是这样:

backgroundQueue.QueueBackgroundWorkItem(async (IService service, CancellationToken ct) =>
{
    //Need to resolve services here...
}

但是这需要很多工作

您也可以考虑使用hangfire这样的专用框架来简化事情。


-1
投票
© www.soinside.com 2019 - 2024. All rights reserved.