Как да разрешите услуги в рамките на фонова задача?

Внедрих BackgroundQueue, както е обяснено тук. Това ще замени старото HostingEnvironment. Освен това преработвам кода, за да използвам Autofac за инжектиране на услуги във фоновата задача.

В момента кодът е следният:

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

    return Ok();
}

backgroundQueue е екземпляр на IBackgroundQueue и е регистриран в Autofac като сингълтън.

Как мога да предам контейнера на Autofac към задачата, за да мога да регистрирам услугите? Или има по-добър начин за регистриране на услуги в задача?

Решение може да прави това:

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

Но това се счита за антипатен.


person Ivan-Mark Debono    schedule 30.03.2020    source източник
comment
Ще трябва да преработите опашката, за да зависи от контейнера и да я имате като параметър на делегата на функцията.   -  person Nkosi    schedule 30.03.2020


Отговори (1)


Ще трябва да управлявате свой собствен 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 от него, защото той ще бъде изхвърлен в края на заявката. Можете да разрешите именуван жизнен обхват, който ще бъде основата на целия ви жизнен обхват на опашката

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, за да улесните нещата.

person Cyril Durand    schedule 30.03.2020
comment
Всички валидни решения и вече си мислех за първото. Аз ще отида по този път. Услугите разрешени ли са в задачата нови екземпляри или споделен екземпляр (защото в момента ги регистрирам като InstancePerLifetimeScope)? - person Ivan-Mark Debono; 31.03.2020
comment
тъй като създавате нов жизнен обхват с помощта на BeginLifetimeScopeи използвате InstancePerLifetimeScope, ще имате нов екземпляр за всяка задача - person Cyril Durand; 31.03.2020