Autofac - 无法创建请求生命周期范围,因为 HttpContext 不可用 - 由于异步代码? [英] Autofac - The request lifetime scope cannot be created because the HttpContext is not available - due to async code?

查看:28
本文介绍了Autofac - 无法创建请求生命周期范围,因为 HttpContext 不可用 - 由于异步代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

长问题:

我刚刚将使用 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() 尽管它是一种反模式.

起初这不起作用,直到我将 MVC Dependency Resolver 设置为同一个容器:

GlobalConfiguration.Configuration.DependencyResolver =新的 AutofacWebApiDependencyResolver(容器);//我不得不引入 Autofac.Mvc 和 Mvc 5.1 集成,但这条线修复了它DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

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

 Solution.Web 项目Bootstrapper.cs 为 web api 和 mvc 注册了依赖解析器.Solution.ClassLib 项目var userRepo = DependencyResolver.Current.GetService();//好的!:)解决方案.WindowsWorkflow 项目var userRepo = DependencyResolver.Current.GetService();//抛出异常:(

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

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

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

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

但它并没有改变异常.进一步分解代码才是真正的罪魁祸首:

var adr = AutofacDependencyResolver.Current;//抛出那个异常

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

解决方案

2014 年 11 月 20 日更新:Autofac.Mvc5 的版本中,自从发布了这个问题,AutofacDependencyResolver.Current 的实现已更新,不再需要 HttpContext.如果您遇到这个问题并找到了这个答案,您可以通过更新到更高版本的 Autofac.Mvc5 来轻松解决问题.但是,我会保留原始答案不变,让人们理解为什么原始提问者有问题.

原答案如下:

<小时>

AutofacDependencyResolver.Current 需要 HttpContext.

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

public static AutofacDependencyResolver Current{得到{return DependencyResolver.Current.GetService();}}

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

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

RequestLifetimeScopeProvider...

获取生命周期范围

public ILifetimeScope GetLifetimeScope(Action configurationAction){如果(HttpContext.Current == null){throw new InvalidOperationException("...");}//...你的代码可能就在那里死了所以我不会//包括源的其余部分.}

它必须像这样工作才能支持像 Glimpse 这样的工具,这些工具动态地包装/代理依赖项解析器以便对其进行检测.这就是为什么您不能将 DependencyResolver.Current 转换为 AutofacDependencyResolver.

几乎任何使用 Autofac.Integration.Mvc.AutofacDependencyResolver 的东西都需要 HttpContext.

这就是您不断收到此错误的原因.如果您没有注册的依赖项 InstancePerHttpRequest 无关紧要 - AutofacDependencyResolver 仍然需要网络上下文.

我猜您使用的另一个工作流应用程序是 MVC 应用程序或始终存在网络上下文的应用程序.

以下是我的建议:

  • 如果您需要使用 Web 上下文之外的组件并且您使用的是 WebApi,请使用 Autofac.Integration.WebApi.AutofacWebApiDependencyResolver.
  • 如果您使用 WCF,请使用标准的 AutofacHostFactory.Container 和该主机工厂实现来解决依赖关系.(WCF 的单例主机潜力有点奇怪,所以每个请求"不是那么简单.)
  • 如果您需要一些不可知"的技术,请考虑 Autofac 的 CommonServiceLocator 实现.它不会创建请求生命周期,但可以解决一些问题.

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

可以相当安全地在服务注册中交替使用 InstancePerApiRequestInstancePerHttpRequest.这两个扩展都使用相同的生命周期范围标记,因此即使底层生命周期范围在一种情况下基于 HttpContext 而另一种情况下基于 IDependencyScope.因此,您可以假设跨应用/应用类型共享一个注册模块,并且它应该做正确的事情.

如果您需要原始 Autofac 容器,请存储您自己对它的引用.与其假设 Autofac 会以某种方式返回该容器,您可能需要在需要时存储对应用程序容器的引用无论出于何种原因,稍后再获取.

公共静态类ApplicationContainer{公共静态 IContainer 容器 { 获取;放;}}//然后当你构建你的解析器时......var 容器 = builder.Build();GlobalConfiguration.Configuration.DependencyResolver =新的 AutofacWebApiDependencyResolver(容器);DependencyResolver.SetResolver(new AutofacDependencyResolver(container));ApplicationContainer.Container = 容器;

这会为您省去很多麻烦.

Short Question: Same as this unanswered problem

Long Question:

I just ported some code over from an MVC 4 + Web Api solution that was using Autofac into my new solution which is also using Autofac but only with Web Api 2 (no MVC 5.1 project, just a web api).

In my previous solution I had MVC4 and Web Api so I had 2 Bootstrapper.cs files, one for each. I copied over just the Web Api bootstrapper for the new project.

Now I have 2 other projects in the new solution that need to pull a dependency. Lets just assume I have to use DependencyResolver.Current.GetService<T>() despite it being an anti-pattern.

At first this was not working until I set the MVC Dependency Resolver to the same container:

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

The strange part is, doing that only fixed it in ONE of those projects! Here's the situation:

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

The exception is: The request lifetime scope cannot be created because the HttpContext is not available.

Now before we start blaming the workflow, just know I had this exact set up working just fine in another solution the workflow was able to use DependencyResolver just fine. So I suspect this had to do with using a newer version of Autofac and the fact that the workflow runs asynchronously (just like the question i linked to regarding async code)

I tried switching all the registration code to use InstancePerLifetimeScope() instead of InstancePerHttpRequest() and trying to create a scope:

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

But it didnt change the exception. Breaking the code down even further here's the exact culprit:

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

Really need to get past this spent too much time stuck. Will reward existing answer with bounty in 2 days

解决方案

UPDATE Nov. 20, 2014: In releases of Autofac.Mvc5 since this question was released, the implementation of AutofacDependencyResolver.Current has been updated to remove the need for an HttpContext. If you are encountering this problem and found this answer, you can potentially easily solve things by updating to a later version of Autofac.Mvc5. However, I will leave the original answer intact for folks to understand why the original question asker was having issues.

Original answer follows:


AutofacDependencyResolver.Current requires an HttpContext.

Walking through the code, AutofacDependencyResolver.Current looks like this:

public static AutofacDependencyResolver Current
{
  get
  {
    return DependencyResolver.Current.GetService<AutofacDependencyResolver>();
  }
}

And, of course, if the current dependency resolver is an AutofacDependencyResolver then it's going to try to do a resolution...

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

Which gets the lifetime scope from a 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.
}

It has to work like that to support tools like Glimpse that dynamically wrap/proxy the dependency resolver in order to instrument it. That's why you can't just cast DependencyResolver.Current as AutofacDependencyResolver.

Pretty much anything using the Autofac.Integration.Mvc.AutofacDependencyResolver requires HttpContext.

That's why you keep getting this error. It doesn't matter if you have no dependencies that are registered InstancePerHttpRequest - AutofacDependencyResolver will still require a web context.

I'm guessing the other workflow app you had where this wasn't an issue was an MVC app or something where there was always a web context.

Here's what I'd recommend:

  • If you need to make use of components outside a web context and you're in WebApi, use the Autofac.Integration.WebApi.AutofacWebApiDependencyResolver.
  • If you're in WCF, make use of the standard AutofacHostFactory.Container and that host factory implementation to resolve dependencies. (WCF is a little weird with its singleton host potential, etc. so "per request" isn't quite as straightforward.)
  • If you need something "agnostic" of technology, consider the CommonServiceLocator implementation for Autofac. It doesn't create request lifetimes, but it may solve some problems.

If you keep those things straight and don't try to use the various resolvers outside their native habitats, as it were, then you shouldn't run into issues.

You can fairly safely use InstancePerApiRequest and InstancePerHttpRequest interchangeably in service registrations. Both of these extensions use the same lifetime scope tag so the notion of an MVC web request and a web API request can be treated similarly even if the underlying lifetime scope in one case is based on HttpContext and the other is based on IDependencyScope. So you could hypothetically share a registration module across apps/app types and it should do the right thing.

If you need the original Autofac container, store your own reference to it. Rather than assuming Autofac will return that container somehow, you may need to store a reference to your application container if you need to get it later for whatever reason.

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;

That will save you a lot of trouble down the road.

这篇关于Autofac - 无法创建请求生命周期范围,因为 HttpContext 不可用 - 由于异步代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆