autofac / Autofac.WebApi

ASP.NET Web API integration for Autofac
Other
36 stars 27 forks source link

DependencyResolutionException thrown when resolving through a delegate returning a .NET Standard 2.0 type #32

Closed rexcfnghk closed 6 years ago

rexcfnghk commented 6 years ago

Summary

Autofac.Core.DependencyResolutionException is thrown when resolving for a component through a delegate that returns a type defined in a .NET Standard 2.0 library, under the context of an classic ASP.NET Web API project.

Steps to reproduce

  1. Create two projects as follows
    • A .NET Standard 2.0 library
    • A classic ASP.NET Web API project targeting .NET Framework 4.6.1
  2. Define an interface and an implementing class pair in the .NET standard library as follows:
    
    public interface IFoo { void DoSomething(); }

public class Foo : IFoo { private readonly HttpClient _httpClient;

public Foo(HttpClient httpClient) => _httpClient = httpClient;

public void DoSomething() { /* No-op */ }

}

3. Reference the .NET Standard library from the ASP.NET Web API project
4. Install Autofac and Autofac.WebApi2 NuGet packages in the ASP.NET Web API project
5. Implement a dummy controller that takes `IFoo` as a constructor parameter, like:
```c#
public class DummyController : ApiController
{
    private readonly IFoo _foo;

    public DummyController(IFoo foo) => _foo = foo;

    public IHttpActionResult Test() => Ok("a");
}
  1. Implement the relevant container registration as follows:

    public class WebApiApplication : System.Web.HttpApplication
    {
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    
        var builder = new ContainerBuilder();
        var config = GlobalConfiguration.Configuration;
    
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
        builder.RegisterWebApiFilterProvider(config);
    
        // Problematic binding here
        builder.Register(c => new Foo(new HttpClient())).As<IFoo>().SingleInstance();
    
        var container = builder.Build();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    }
    }
  2. Run the ASP.NET Web API application and visit /api/dummy/test.

Expected behaviour

"a" is returned.

Actual behaviour

Autofac.Core.DependencyResolutionException is thrown.

Stack trace:

An error has occurred.An error occurred when trying to create a controller of type 'DummyController'. Make sure that the controller has a parameterless public constructor.System.InvalidOperationException   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
   at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()An error has occurred.An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = DummyController (ReflectionActivator), Services = [AutofacNetStandard.Web.Controllers.DummyController], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = OwnedByLifetimeScope ---> An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = Foo (DelegateActivator), Services = [AutofacNetStandara.Library.IFoo], Lifetime = Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope ---> Method not found: 'Void AutofacNetStandara.Library.Foo..ctor(System.Net.Http.HttpClient)'. (See inner exception for details.) (See inner exception for details.)Autofac.Core.DependencyResolutionException   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
   at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType)
   at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType)
   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)
   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)An error has occurred.An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = Foo (DelegateActivator), Services = [AutofacNetStandara.Library.IFoo], Lifetime = Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope ---> Method not found: 'Void AutofacNetStandara.Library.Foo..ctor(System.Net.Http.HttpClient)'. (See inner exception for details.)Autofac.Core.DependencyResolutionException   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.b__5_0()
   at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Activators.Reflection.AutowiringParameter.<>c__DisplayClass0_0.b__0()
   at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()
   at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)An error has occurred.Method not found: 'Void AutofacNetStandara.Library.Foo..ctor(System.Net.Http.HttpClient)'.System.MissingMethodException   at AutofacNetStandard.Web.WebApiApplication.<>c.b__0_0(IComponentContext c)
   at Autofac.RegistrationExtensions.<>c__DisplayClass5_0`1.b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass0_0`1.b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
tillig commented 6 years ago

Have you tried different things to narrow down what might be happening here? The exception is saying it can't find the constructor specified in the delegate. Since it compiles that seems like a runtime type binding problem. Autofac doesn't actually do that work, the .NET runtime does... Which means the exception is surfacing through Autofac but it's just disguising the underlying problem.

Notice the source of the missing method exception (5 lines up from bottom of stack trace) is the generated code from the delegate.

Some things to try:

The idea here is to try to determine the root cause that is being covered up by the exception. Maybe it's a missing runtime directive or something else, but hopefully some additional tests can shed light.

rexcfnghk commented 6 years ago

Thanks @tillig , it seems to be an integration issue with .NET Standard and ASP.NET, I have submitted another issue to the .NET Standard repo: https://github.com/dotnet/standard/issues/613

tillig commented 6 years ago

I'm glad you were able to track down the root cause. I'll follow that issue to see what happens, but since it's not an Autofac issue, I'll close this.

rexcfnghk commented 6 years ago

Sure, thanks for the suggestions that helped me track down the issue!