【问题标题】:Autofac - The request lifetime scope cannot be created because the HttpContext is not available - due to async code?Autofac - 无法创建请求生命周期范围,因为 HttpContext 不可用 - 由于异步代码?
【发布时间】:2014-02-15 23:00:13
【问题描述】:

小问题:Same as this unanswered problem

长问题:

我刚刚将一些代码从使用 Autofac 的 MVC 4 + Web Api 解决方案移植到我的新解决方案中,该解决方案也使用 Autofac 但仅使用 Web Api 2(没有 MVC 5.1 项目,只是一个 Web api)。

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

现在我在新解决方案中有 2 个需要提取依赖项的其他项目。假设我必须使用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));

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

 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 :(

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

现在,在我们开始责怪工作流之前,只要知道我在另一个解决方案中这个确切的设置工作得很好,工作流能够很好地使用 DependencyResolver。所以我怀疑这与使用较新版本的 Autofac 以及工作流异步运行的事实有关(就像我链接到的关于异步代码的问题)

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

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

但它并没有改变异常。进一步分解代码是罪魁祸首:

var adr = AutofacDependencyResolver.Current; //Throws that exception 

真的需要克服这个花太多时间卡住的问题。将在 2 天内以赏金奖励现有答案

【问题讨论】:

  • 但是 HttpContext 确实存在于您的 Workflow 项目中吗?你可以创建一个容器并尝试直接从容器中解析IUserRepo,而不是去.CurrentInstancePerLifetimeScope() 也比 InstancePerHttpRequest 好。

标签: c# asp.net-mvc asp.net-web-api autofac dependency-resolver


【解决方案1】:

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>();
  }
}

当然,如果当前的依赖解析器AutofacDependencyResolver,那么它会尝试进行解析...

public object GetService(Type serviceType)
{
  return RequestLifetimeScope.ResolveOptional(serviceType);
}

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.
}

它必须像这样工作才能支持像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 实现。它不会创建请求生命周期,但可能会解决一些问题。

如果您保持这些事情的正确性,并且不尝试在其原生栖息地之外使用各种解析器,那么您就不应该遇到问题。

可以相当安全地在服务注册中互换使用 InstancePerApiRequestInstancePerHttpRequest 这两个扩展都使用相同的生命周期范围标记,因此 MVC 的概念即使底层生命周期范围在一种情况下基于HttpContext 而另一种情况下基于IDependencyScope,也可以类似地处理 Web 请求和 Web API 请求。因此,您可以假设跨应用/应用类型共享一个注册模块,它应该做正确的事情。

如果您需要原始 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;

这将为您省去很多麻烦。

【讨论】:

  • 我还没有时间尝试这个,但我很确定它会解决我的问题,所以答案就是这样。很好的解释,谢谢!
  • 不幸的是它不起作用:(:从请求实例的范围内看不到标签匹配“AutofacWebRequest”的范围。这通常表明正在注册为每个HTTP请求的组件由 SingleInstance() 组件(或类似场景)请求。在 Web 集成下,始终从 DependencyResolver.Current 或 ILifetimeScopeProvider.RequestLifetime 请求依赖项,而不是从容器本身请求。
  • 等我明白了!如果我将所有内容都注册为 InstancePerLifetimeScope(),则可以使用。好东西,谢谢
  • 这真的是一个有效的答案。因为我在做这个``` var type = Type.GetType(identifier);返回 AutofacDependencyResolver.Current.GetService(type); ``` 但是仍然得到缺少的 HttpContext 异常。我也在 Autofac 中看到了原因。代码:github.com/autofac/Autofac.Mvc/blob/develop/src/… 那么我该如何解决这个问题。
  • @Saab 是的,这是一个有效的答案。您可能需要发布一个新问题。缺少 HttpContext 可能是因为您在没有上下文时调用 GetService,而不是因为 AutofacDependencyResolver.CurrentRead this FAQ 然后如果您仍然遇到问题,请在您的代码中发布一个新问题。
【解决方案2】:

我的假设:

  1. 您正在与 MVC 项目分开的 Thread/AppDomain 中运行工作流项目。
  2. IUserRepo 依赖于 HttpContext

如果我的假设正确,Workflow 项目将不知道HttpContext.Current

WindowsWorkflow 项目一直在运行(如果我理解正确的话——实际上并没有使用这项技术)。 MVC 基于 HTTP 请求。 HttpContext.Current 仅在有请求进入时填充。如果没有请求 - 此变量为空。如果没有请求,但 Workflow 实例正在尝试访问 HttpContext,会发生什么情况?正确 - 空引用异常。或者在你的情况下依赖解析异常。

你需要做什么:

  1. 将容器注册分离到模块中 - 所有域类的域模块。然后是 MVC 模块:用于所有 MVC 细节,例如 User.CurrentHttpContext.Current。以及带有所有工作流特定实现的工作流模块(如果需要)。
  2. 在工作流程初始化时,使用域和工作流程模块创建 autofac 容器,排除 MVC 依赖项。对于 MVC 容器 - 在没有工作流模块的情况下创建它。
  3. 对于IUserRepo,创建不依赖于 HttpContext 的实现。这可能是最有问题的做法。

我在 Azure 中为 Quartz.Net 执行做了类似的事情。请参阅我的博客文章:http://tech.trailmax.info/2013/07/quartz-net-in-azure-with-autofac-smoothness/。这篇文章不会直接帮助你,但会解释我拆分 autofac 模块的原因。

根据评论更新: WebApi 在这里澄清了很多事情。 WebApi 请求不会通过与 MVC 请求相同的管道。并且 WebApi 控制器无权访问 HttpContext。见this answer

现在,根据您在 wepApi 控制器中执行的操作,您可能需要更改 IUserRepo 实现,以便能够同时使用 MVC 和 WebApi。

【讨论】:

  • 谢谢,我会调查你的答案。但是我很抱歉没有提到这个特定的工作流程只是从一个 api 请求中启动的。因此,用户单击电子邮件中的链接以验证他们的帐户,Web api 控制器调用 workflow.VerifyRegistration(verificationCodeFromUrl),然后在 HttpContext 可用时启动工作流。我需要能够使用该上下文,因为我在之前的实现中已经成功。
  • 感谢您回来。现在,对于没有 HttpContext 的 Web Api,这很有意义,但是您建议我用这些更新的知识做什么?工作流实际上并不需要 HttpContext,它不会在任何地方引用它,它只需要像其他任何解决 IUserRepo 依赖项一样,所以我仍然不确定它为什么抱怨。为什么我需要一个 HttpContext 来解决依赖关系?为什么我不能在没有一个的情况下解决它。当然它会在不同的范围内,但我已经通过使用 DependencyResolver 接受了这个警告,这是一个我可以接受的边缘情况,因为我什至不调用 uow.Commit()
  • UserRepository 及其任何依赖项都不依赖于 HttpContext
  • 现在这是一个很好的问题!可以发一下你的注册码吗?
【解决方案3】:

我们目前的情况是,我们的测试遇到了“缺少 httpcontext”问题,但由于版本限制,我们还不能使用上述出色的建议。

我们解决它的方法是在我们的测试设置中创建一个“模拟”http 上下文: 见:Mock HttpContext.Current in Test Init Method

【讨论】:

    【解决方案4】:

    为了解决这个问题,我再点击同一个按钮几次,通常不超过 4 次。如果是这样,你可以给用户发一条消息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多