Autofac - 无法创建请求生命周期范围,因为HttpContext不可用 - 由于异步代码?

par*_*ent 14 c# asp.net-mvc autofac asp.net-web-api dependency-resolver

简短问题:与此未解决的问题相同

长问题:

我刚刚从MVC 4 + Web Api解决方案中移植了一些代码,该解决方案使用Autofac进入我的新解决方案,该解决方案也使用Autofac但仅使用Web Api 2(没有MVC 5.1项目,只有web api).

在我之前的解决方案中,我有MVC4和Web Api所以我有2个Bootstrapper.cs文件,每个文件一个.我复制了新项目的Web Api引导程序.

现在我在新解决方案中有两个需要依赖的项目.让我们假设我必须使用DependencyResolver.Current.GetService<T>()尽管它是一个反模式.

起初,直到我将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));
Run Code Online (Sandbox Code Playgroud)

奇怪的是,这样做只能在其中一个项目中修复它!情况如下:

 Solution.Web project
      Bootstrapper.cs that registers both dependency resolvers for web api and mvc.

 Solution.ClassLib project
      var userRepo = DependencyResolver.Current.GetService<IUserRepo>(); //Good! :)

 Solution.WindowsWorkflow project
      var userRepo = DependencyResolver.Current.GetService<IUserRepo>(); //Throws exception :(
Run Code Online (Sandbox Code Playgroud)

例外情况是:无法创建请求生存期范围,因为HttpContext不可用.

现在,在我们开始指责工作流程之前,只知道我已经在另一个解决方案中使用了这个确切的设置,工作流程能够正常使用DependencyResolver.所以我怀疑这与使用更新版本的Autofac以及工作流异步运行的事实有关(就像我关于异步代码链接的问题一样)

我尝试使用切换所有注册代码InstancePerLifetimeScope()而不是InstancePerHttpRequest()尝试创建范围:

using (var c= AutofacDependencyResolver.Current
                     .ApplicationContainer.BeginLifetimeScope("AutofacWebRequest"))
{
   var userRepo = DependencyResolver.Current.GetServices<IUserRepo>();
}
Run Code Online (Sandbox Code Playgroud)

但它没有改变例外.进一步打破代码这里是确切的罪魁祸首:

var adr = AutofacDependencyResolver.Current; //Throws that exception 
Run Code Online (Sandbox Code Playgroud)

真的需要通过这个花费太多时间卡住.将在2天内以赏金奖励现有答案

Tra*_*lig 25

更新2014年11月20日:Autofac.Mvc5此问题发布后的版本中,AutofacDependencyResolver.Current已更新实施,以消除对此问题的需求HttpContext.如果您遇到此问题并找到了此答案,则可以通过更新到更高版本来轻松解决问题Autofac.Mvc5.但是,我会保留原始答案,以便人们了解原始提问者为何遇到问题.

原始答案如下:


AutofacDependencyResolver.Current需要一个HttpContext.

遍历代码,AutofacDependencyResolver.Current看起来像这样:

public static AutofacDependencyResolver Current
{
  get
  {
    return DependencyResolver.Current.GetService<AutofacDependencyResolver>();
  }
}
Run Code Online (Sandbox Code Playgroud)

而且,当然,如果当前的依赖解析器一个,AutofacDependencyResolver那么它将尝试做出解决方案......

public object GetService(Type serviceType)
{
  return RequestLifetimeScope.ResolveOptional(serviceType);
}
Run Code Online (Sandbox Code Playgroud)

RequestLifetimeScopeProvider... 获得生命范围

public ILifetimeScope GetLifetimeScope(Action<ContainerBuilder> 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.
}
Run Code Online (Sandbox Code Playgroud)

它必须像这样工作,以支持像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对其单身主机潜力等有点奇怪,因此"每个请求"并不那么简单.)
  • 如果您需要某些技术"不可知",请考虑CommonServiceLocatorAutofac 的实现.它不会创建请求生存期,但它可以解决一些问题.

如果你保持这些东西是正确的,不要试图在它们原生境外使用各种旋转变压器,那么你就不应该遇到问题.

可以在服务注册中相当安全地使用InstancePerApiRequestInstancePerHttpRequest互换.这两个扩展都使用相同的生命周期范围标记,因此即使一个案例中的基础生命周期范围基于HttpContext另一个基于生命周期范围,也可以类似地处理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;
Run Code Online (Sandbox Code Playgroud)

这将为您节省很多麻烦.