Autofac作用域生命周期问题

5

我有一个ASP.NET MVC应用程序,其中我使用InstancePerHttpRequest范围注册了一个组件。

builder.RegisterType<Adapter>().As<IAdapter>().InstancePerHttpRequest();

接下来我有一个异步代码片段,我在其中解析Adapter组件。

以下代码是简化的

Task<HttpResponseMessage> t = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>

      // IHandleCommand<T> takes an IAdapter as contructor argument
      var h = DependencyResolver.Current.GetServices<IHandleCommand<T>>();
);

上面的代码抛出了一个异常:The request lifetime scope cannot be created because the HttpContext is not available.。因此,我进行了一些研究,并找到了这个答案: https://dev59.com/CV7Va4cB1Zd3GeqPMbYK#8663021。然后我将解析代码调整为以下内容。
 using (var c= AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope(x => x.RegisterType<DataAccessAdapter>().As<IDataAccessAdapter>).InstancePerLifetimeScope()))
 {
       var h = DependencyResolver.Current.GetServices<IHandleCommand<T>>();
 }

但是异常仍然存在。请求生命周期范围无法创建,因为HttpContext不可用。

我是否遗漏了什么?


你最终解决了这个问题吗?我在将一些代码从之前(工作正常的)Web Api移植过来后遇到了同样的问题。在这个解决方案中,我正在使用Web APi 2和Autofac web api 2集成。我觉得这次升级引起了这个问题。 - parliament
议会,这里的问题是HttpContext是MVC概念,但请求是通过WebApi完成的。而WebApi并不直接访问HttpContext。我假设所请求类型的一个依赖项是HttpContext。因此,Autofac无法解析HttpContext,从而导致异常。 - trailmax
4个回答

4
Autofac试图从MVC依赖项解析器中解析容器,如果您有一个异步操作,httpContext将不可用,因此DependencyResolver也将不可用。
一种选择是将容器放在静态变量或适当的实例中,并为该操作创建一个上下文范围。
public static IContainer Container

一旦您设置好构建器,复制容器。
public class ContainerConfig
{
    public static IContainer Container;
    public static void RegisterComponents()
    {
        var builder = new ContainerBuilder();
        builder.RegisterInstance(new Svc()).As<ISvc>();
        Container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(Container ));
    }
}    

然后,在解决问题时,请使用静态容器配置来创建所需的实例。
using (var scope = ContainerConfig.Container.BeginLifetimeScope())
{
       result = ContainerConfig.Container.Resolve<T>();
}

希望这能有所帮助


4
您可以尝试像这样做:

您可以尝试类似以下方式:

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

1
如果您无法访问System.Web.Http,则无法使用DependencyResolver.Current。您需要存储容器并从中解析依赖项:
//On Startup Class
public static IContainer Container { get; private set; }

public void Configuration(IAppBuilder app)
{
   ...
   var builder = new ContainerBuilder();
   builder.RegisterModule([yourmodules]);
   ...
   var container = builder.Build();
   Container = container;
}

然后,当你需要使用实例时:

using (var scope = Container.BeginLifetimeScope())
{                                
   YourInterfaceImpl client = Container.Resolve<YourInterface>();
   ...
}

希望这可以帮到你!

1
在我的情况下,我在WebAPI启动时实例化了所有类型,以尽早捕获任何故障。此时还没有请求,所以如果一个类型被注册为InstancePerRequest,我会收到这个错误信息:

无法从请求实例所在的范围中看到与“AutofacWebRequest”匹配的标记的范围。

基于@KozhevnikovDmitry的回答,以下是我让它工作的方法:
using (var scope = container.BeginLifetimeScope("AutofacWebRequest"))
{
    foreach (Service item in container.ComponentRegistry.Registrations.SelectMany(x => x.Services))
    {
        Type type = item is TypedService ts ? ts.ServiceType
                  : item is KeyedService ks ? ks.ServiceType
                  : throw new Exception($"Unknown type `{item.Description}`");
        try
        {
            scope.Resolve(type);
        }
        catch (Exception ex)
        {
            _log.Debug($"Error instantiating type `{type.FullName}`", ex);
            throw;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接