![](/img/trans.png)
[英]Calling services scoped per request from reccuring background tasks using Autofac and Quartz in MVC 5
[英]Autofac not working with background tasks
我有一个任务需要在后台的单独线程中运行,我正在使用 SignalR 报告进度。 这在前段时间起作用了,并且我进行了一些代码修改,但是对于我现在收到的错误,我完全不知所措:
“从请求实例的范围中看不到具有匹配 'AutofacWebRequest' 标签的范围。这通常表明 SingleInstance() 组件(或类似场景)正在请求注册为每个 HTTP 请求的组件。在 Web 集成下,始终从 DependencyResolver.Current 或 ILifetimeScopeProvider.RequestLifetime 请求依赖项,而不是从容器本身请求依赖项。”
任何帮助是极大的赞赏!
public ActionResult DoAction(IEnumerable<string> items){
//...
Func<CancellationToken, Task> taskFunc = CancellationToken => performAction(items);
HostingEnvironment.QueueBackgroundWorkItem(taskFunc);
//...
}
private async Task performAction(IEnumerable<string> items){
var svc = AutofacDependencyResolver.Current.AppicationContainer.BeginLifetimeScope().Resolve<MyService>();
svc.Method(items);
}
public class MyService{
private EntityContext db;
public MyService(EntityContext db){
this.db = db;
}
}
在我的 Startup.Container.cs 文件中:
builder.RegisterType<MyService>().As<MyService>().InstancePerLifetimeScope();
builder.RegisterType<EntityContext>().InstancePerRequest();
我最近使用this answer和this answer的帮助实现了类似的功能。 您需要创建一个新的生命周期范围 - 听起来您在 Web 应用程序中执行此操作,因此您需要通过 per-request 标记(下面的示例)创建范围。
另一个(非 StackOverflow) 答案提供了类似的建议。
public Task Run<T>(Action<T> action)
{
Task.Factory.StartNew(() =>
{
using (var lifetimeScope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
{
var service = lifetimeScope.Resolve<T>();
action(service);
}
});
return Task.FromResult(0);
}
我做了类似于@Chima Osuji 的事情,但我认为他的回答有些不对劲,所以我将描述我的解决方案并进行解释。
public class BackgroundTaskFactory : IBackgroundTaskFactory
{
private ILifetimeScope lifetimeScope;
public BackgroundTaskFactory(ILifetimeScope lifetimeScope)
{
this.lifetimeScope = lifetimeScope;
}
public Task Run<T>(Action<T> action)
{
Task task = Task.Factory.StartNew(() =>
{
using (var lifetimeScope = this.lifetimeScope.BeginLifetimeScope())
{
var service = lifetimeScope.Resolve<T>();
action(service);
}
});
return task;
}
}
需要指出的是,我的 Run 方法正在返回在 Task.Factory.StartNew 上创建的任务。 这样有人等待结果,他得到了正确的任务。 在其他解决方案中,他们返回 Task.FromResult(0) ,它返回一个虚拟任务。
BeginLifetimeScope 创建一个新范围作为注入范围的子级。 如果注入的作用域是与 Web 请求关联的 InstancePerLifetimeScope,则一旦 Web 请求作用域被释放,这个新作用域也将被释放,并且会出错。 子作用域的寿命不能长于其父作用域。 解决方案? 将 BackgroundTaskFactory 注册为 singleton 。 当你这样做时,注入的生命周期范围将是根范围,在应用程序被释放之前不会被释放。
containerBuilder.RegisterType< BackgroundTaskFactory >() .As< IBackgroundTaskFactory >() .SingleInstance();
基于上述代码的更新答案:
用法:
public class ServiceModule :Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AutoFac.AsyncRunner>().As<AutoFac.IAsyncRunner>().SingleInstance();
}
}
public class Controller
{
private AutoFac.IAsyncRunner _asyncRunner;
public Controller(AutoFac.IAsyncRunner asyncRunner)
{
_asyncRunner = asyncRunner;
}
public void Function()
{
_asyncRunner.Run<IService>((cis) =>
{
try
{
//do stuff
}
catch
{
// catch stuff
throw;
}
});
}
}
界面:
public interface IAsyncRunner
{
Task Run<T>(Action<T> action);
}
班上:
public class AsyncRunner : IAsyncRunner
{
private ILifetimeScope _lifetimeScope { get; set; }
public AsyncRunner(ILifetimeScope lifetimeScope)
{
//Guard.NotNull(() => lifetimeScope, lifetimeScope);
_lifetimeScope = lifetimeScope;
}
public Task Run<T>(Action<T> action)
{
Task.Factory.StartNew(() =>
{
using (var lifetimeScope = _lifetimeScope.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
{
var service = lifetimeScope.Resolve<T>();
action(service);
}
});
return Task.FromResult(0);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.