如何解决使用异步任务和拥有的实例升级 Autofac 时出现问题
在我的 ASP.NET MVC 应用程序中从 4.9.2 升级到 5.2 后,我遇到了 Autofac 问题。
我在控制器中使用 Func<Owned<T>>
工厂模式,因为控制器操作启动一个长时间运行的任务,并且运行时间比请求存在的时间长。在该任务中,我正在解决其他实例。
这在 Autofac 4.9.2 中运行良好。但是在升级到 Autofac 5.2 后,父生命周期范围 (AutofacWebRequest) 被释放,并且无法再在拥有的实例中解析实例。
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
我可以做些什么来解决这个问题,或者有什么最佳实践吗?
控制器代码:
private readonly Func<Owned<IBusinessLogic>> _businessLogicFactory;
public ActionResult Index()
{
var businessLogic = _businessLogicFactory();
var unitOfWorkFactory = _unitOfWorkFactory;
Task.Run(() =>
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
using (businessLogic)
{
var task = businessLogic.Value.DoHardBusinessAsync();
task.Wait();
}
});
return View();
}
业务逻辑代码(也使用工厂):
public class BusinessLogic : IBusinessLogic
{
private readonly Func<Owned<OtherBusinessLogic>> _otherBusinessLogicFactory;
public BusinessLogic(Func<Owned<OtherBusinessLogic>> otherBusinessLogicFactory)
{
_otherBusinessLogicFactory = otherBusinessLogicFactory;
}
public async Task DoHardBusinessAsync()
{
using (var otherBusiness = _otherBusinessLogicFactory())
{
await otherBusiness.Value.DoHardBusinessAsync();
}
}
}
解决方法
您可以尝试创建一个新的生命周期范围,该范围独立于要用于长时间运行的任务的请求范围
Task.Run(() =>
{
using (var scope = container.BeginLifetimeScope())
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
using (businessLogic)
{
var task = businessLogic.Value.DoHardBusinessAsync();
task.Wait();
}
}
});
查看此问题以了解有关如何获取容器的想法 Retrieving Autofac container to resolve services
,@NataliaMuray 的方法很棒 - 缺点是它倾向于鼓励服务定位器样式解析而不是构造函数注入。这往往会“隐藏”依赖关系,从而更难识别给定类的依赖关系。
一种潜在的解决方案是引入依赖项的概念,该概念明确表示它包装了另一个依赖项,您希望在正常 Web 请求的生命周期范围之外解析该依赖项。
代码可能类似于:
public class AsyncRunner : IAsyncRunner
{
public ExecutionResult TryExecute<TService>(Action<TService> toEvaluate,string @exceptionErrorMessage,int timeoutMilliseconds,string additionalErrorInformation = "")
{
try
{
var task = new Task(() =>
{
using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<TService>();
toEvaluate(service);
}
});
task.ContinueWith(t => { /* logging here */,TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously).SuppressExceptions();
task.Start();
var completedWithinTime = task.Wait(timeoutMilliseconds);
return completedWithinTime ? ExecutionResult.Ok : ExecutionResult.TimedOut;
}
catch (Exception e)
{
/* logging here */
return ExecutionResult.ThrewException;
}
}
}
同时向 Autofac 注册 IAsyncRunner。
然后是你的依赖,而不是
private readonly Func<Owned<IBusinessLogic>> _businessLogicFactory;
会
private readonly IAsyncRunner<IBusinessLogic>> _businessLogic;
而不是:
var businessLogic = _businessLogicFactory();
var unitOfWorkFactory = _unitOfWorkFactory;
Task.Run(() =>
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
using (businessLogic)
{
var task = businessLogic.Value.DoHardBusinessAsync();
task.Wait();
}
});
应该是:
//var businessLogic = _businessLogicFactory();
var unitOfWorkFactory = _unitOfWorkFactory;
Task.Run(() =>
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
_businessLogic.TryExecute(z => {
var task = z.Value.DoHardBusinessAsync();
task.Wait();
});
});
这种风格的优点是属性和构造函数注入明确了依赖是什么,以及它们是如何使用的(即声明明确它将在标准生命周期范围的上下文之外解析)。请注意,您不需要在我的建议中使用 Owned
(处理手动构建的生命周期范围就足够了)。我已经删除了 Func
的使用,但如果您真的需要它和我的建议,您可以使用 Func
或 Lazy
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。