Autofac - The request lifetime scope cannot be created because the HttpContext is not available - due to async code?

前端 未结 3 795
小蘑菇
小蘑菇 2020-12-06 05:08

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

3条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-06 05:57

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

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

提交回复
热议问题