Как разрешить услуги в фоновом режиме? - PullRequest
1 голос
/ 30 марта 2020

Я реализовал BackgroundQueue, как объяснено здесь . Это заменит старую HostingEnvironment. Кроме того, я делаю рефакторинг кода, чтобы использовать Autofa c для добавления сервисов в фоновую задачу.

В настоящее время код выглядит так:

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

    return Ok();
}

backgroundQueue - это экземпляр IBackgroundQueue и зарегистрирован в Autofa c как одноэлементный.

Как передать контейнер Autofa c в задачу, чтобы я мог зарегистрировать службы? Или есть лучший способ зарегистрировать службы в задаче?

Решение может заключаться в следующем:

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

Но это считается антипатентом.

1 Ответ

2 голосов
/ 31 марта 2020

Вам придется управлять своим собственным 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 из него, потому что он будет удален в конце запроса. Вы можете разрешить именованный жизненный цикл, который будет root всего объема жизни вашей очереди

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 , чтобы упростить задачу.

...