我有一个任务需要在后台的一个单独的线程中运行,我使用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();
答案 0 :(得分:4)
我最近使用此answer和此answer的帮助实现了类似的功能。您需要创建一个新的生命周期范围 - 这听起来像是在Web应用程序中执行此操作,因此您需要通过每个请求标记创建范围(下面的示例)。
另一个(非StackOverflow)answer提供了类似的建议。
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);
}
答案 1 :(得分:0)
基于以上代码的最新答案:
用法:
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);
}
}
答案 2 :(得分: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注册为单例。这样做时,注入的生命周期范围将是根范围,直到处置该应用程序后该根范围才被处置。
containerBuilder.RegisterType