我已经按照here的说明实现了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));
但是这被认为是反专利。
您将必须在任务中管理自己的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这样的专用框架来简化事情。
我认为您应该为此使用HostedService。它支持DI。
在控制器方法内,您可以将实体添加到数据库中。在托管服务中,您可以轮询数据库上的操作。