Autofac – 无法创建请求生命周期范围,因为HttpContext不可用 – 由于异步代码?
简短问题: 与此未解决的问题相同
长问题:
我刚刚从MVC 4 + Web Api解决方案中移植了一些代码,该解决方案使用Autofac进入我的新解决方案,该解决方案也使用Autofac但仅使用Web Api 2(没有MVC 5.1项目,只有web api)。
在我以前的解决方案中,我有MVC4和Web Api所以我有2个Bootstrapper.cs文件,每个文件一个。 我复制了新项目的Web Api引导程序。
现在我在新解决方案中有2个其他需要依赖项目的项目。 让我们假设我必须使用DependencyResolver.Current.GetService()
尽管它是一个反模式。
起初,在我将MVC依赖关系解析器设置为同一容器之前,这是行不通的:
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); //I had to pull in Autofac.Mvc and Mvc 5.1 integration but this line fixed it DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
奇怪的是,这样做只会修复其中一个项目! 这是情况:
Solution.Web project Bootstrapper.cs that registers both dependency resolvers for web api and mvc. Solution.ClassLib project var userRepo = DependencyResolver.Current.GetService(); //Good! :) Solution.WindowsWorkflow project var userRepo = DependencyResolver.Current.GetService(); //Throws exception :(
例外情况是:无法创建请求生存期范围,因为HttpContext不可用。
现在,在我们开始指责工作流程之前,只知道我已经在另一个解决方案中正常工作,工作流程能够正常使用DependencyResolver。 所以我怀疑这与使用更新版本的Autofac以及工作流异步运行的事实有关(就像我关于异步代码链接的问题一样)
我尝试切换所有注册代码以使用InstancePerLifetimeScope()
而不是InstancePerHttpRequest()
并尝试创建范围:
using (var c= AutofacDependencyResolver.Current .ApplicationContainer.BeginLifetimeScope("AutofacWebRequest")) { var userRepo = DependencyResolver.Current.GetServices(); }
但它没有改变例外。 进一步打破代码这里是确切的罪魁祸首:
var adr = AutofacDependencyResolver.Current; //Throws that exception
真的需要过去花这么多时间卡住。 将在2天内以赏金奖励现有答案
更新2014年11月20日:自问题发布以来,在Autofac.Mvc5
版本中, Autofac.Mvc5
的实现已更新,无需使用HttpContext
。 如果您遇到此问题并找到了此答案,则可以通过更新到更高版本的Autofac.Mvc5
来轻松解决问题 。 但是,我会保留原始答案,以便人们了解原始提问者为何遇到问题。
原始答案如下:
AutofacDependencyResolver.Current
需要一个HttpContext
。
AutofacDependencyResolver.Current
代码, AutofacDependencyResolver.Current
看起来像这样:
public static AutofacDependencyResolver Current { get { return DependencyResolver.Current.GetService(); } }
当然,如果当前依赖项解析器是 AutofacDependencyResolver
那么它将尝试进行解析……
public object GetService(Type serviceType) { return RequestLifetimeScope.ResolveOptional(serviceType); }
从RequestLifetimeScopeProvider
获取生命周期范围……
public ILifetimeScope GetLifetimeScope(Action configurationAction) { if (HttpContext.Current == null) { throw new InvalidOperationException("..."); } // ...and your code is probably dying right there so I won't // include the rest of the source. }
它必须像这样工作,以支持像Glimpse这样的工具,它动态地包装/代理依赖解析器以便对它进行检测。 这就是为什么你不能只将DependencyResolver.Current as AutofacDependencyResolver
。
几乎任何使用Autofac.Integration.Mvc.AutofacDependencyResolver
东西都需要HttpContext
。
这就是你不断收到这个错误的原因。 如果您没有注册InstancePerHttpRequest
依赖项也没关系 – AutofacDependencyResolver
仍然需要Web上下文。
我猜你的其他工作流程应用程序,这不是一个问题是MVC应用程序或总是有Web上下文的东西。
这是我建议的:
- 如果您需要使用Web上下文之外的组件并且您在WebApi中,请使用
Autofac.Integration.WebApi.AutofacWebApiDependencyResolver
。 - 如果您在WCF中,请使用标准的
AutofacHostFactory.Container
和主机工厂实现来解决依赖关系。 (WCF对其单身主机潜力等有点奇怪,所以“每个请求”并不那么简单。) - 如果您需要某些技术“不可知”,请考虑Autofac的CommonServiceLocator实现。 它不会创建请求生命周期,但它可以解决一些问题。
如果你保持这些东西是正确的,不要试图使用原生境外的各种解决方案,那么你就不应该遇到问题。
您可以相当安全地在服务注册中交替使用InstancePerApiRequest
和InstancePerHttpRequest
。 这两个扩展都使用相同的生命周期范围标记,因此即使一个案例中的基础生命周期范围基于HttpContext
而另一个基于IDependencyScope
也可以类似地处理MVC Web请求和Web API请求的IDependencyScope
。 因此,您可以假设在应用/应用类型之间共享注册模块,它应该做正确的事情。
如果您需要原始的Autofac容器,请存储您自己的引用。 而不是假设Autofac将以某种方式返回该容器,如果您因以下任何原因需要稍后获取它,则可能需要存储对应用程序容器的引用。
public static class ApplicationContainer { public static IContainer Container { get; set; } } // And then when you build your resolvers... var container = builder.Build(); GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); ApplicationContainer.Container = container;
这将为您节省很多麻烦。
我的假设:
- 您正在从MVC项目的单独Thread / AppDomain中运行工作流项目。
-
IUserRepo
依赖于HttpContext
如果我的假设正确,Workflow项目将不知道HttpContext.Current
。
WindowsWorkflow项目一直在运行(如果我理解正确 – 实际上并没有使用这种技术)。 MVC基于HTTP请求的位置。 只有在有请求进入时才会填充HttpContext.Current
。如果没有请求 – 此变量为null。 如果没有请求会发生什么,但Workflow实例正在尝试访问HttpContext
? 正确 – 空引用exception。 或者在你的情况下依赖解析例外。
你需要做什么:
- 将容器注册分成模块 – 所有域类的域模块。 然后是MVC模块:用于所有MVC细节,如
User.Current
或HttpContext.Current
。 和Workflow模块(如果需要)以及所有特定于Workflow的实现。 - 在Workflow初始化时,使用域和工作流模块创建autofac容器,排除MVC依赖项。 对于MVC容器 – 无需工作流程模块即可创建它。
- 对于
IUserRepo
创建不依赖于HttpContext的实现。 这可能是最有问题的。
我已经为Azure中的Quartz.Net执行做了类似的事情。 请参阅我的博客文章: http : //tech.trailmax.info/2013/07/quartz-net-in-azure-with-autofac-smoothness/ 。 这篇文章不会直接帮助你,但解释了我拆分autofac模块的原因。
根据评论更新: WebApi澄清了很多事情。 WebApi请求不会通过与MVC请求相同的管道。 WebApi控制器无法访问HttpContext。 看到这个答案 。
现在,根据您在wepApi控制器中的操作,您可能希望更改IUserRepo
实现以便能够使用MVC和WebApi。
我们目前处于这样的情况:我们的测试遇到了“缺少httpcontext”问题但由于版本限制而无法使用上述优秀建议。
我们解决它的方法是在我们的测试设置中创建一个’模拟’http上下文:参见: 测试初始化方法中的Mock HttpContext.Current