你所看到的发生的事情与这个问题 https://stackoverflow.com/a/15910758/264697。简而言之,Web API 会调用它的默认值IHttpControllerActivator
请求新控制器实例的实现。该实例将调用您的DependencyResolver.GetService
方法。该方法会将调用转发到 MS.DI 的GetService
方法。然而,自从您没有将控制器注册到 MS.DI 容器中,它将返回null
。这会导致默认IHttpControllerActivator
尝试使用反射创建控制器,但这需要一个默认构造函数。由于控制器没有,这会导致相当神秘的异常消息。
因此,快速的解决方案是注册您的控制器,例如:
services.AddTransient<TestController>();
然而,这只能部分解决你的问题,因为your IDependencyResolver
实施被破坏。它以一种丑陋的方式被破坏,因为它一开始似乎可以工作,但会导致内存泄漏,因为你always从根容器解析,而不是从范围解析。这将导致您解析的控制器实例(和其他一次性瞬态组件)在应用程序的生命周期内保持引用。
要解决此问题,您应该更改您的IDependencyResolver
实施如下:
public class DependencyResolver : IDependencyResolver
{
private readonly IServiceProvider provider;
private readonly IServiceScope scope;
public DependencyResolver(ServiceProvider provider) => this.provider = provider;
internal DependencyResolver(IServiceScope scope)
{
this.provider = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() =>
new DependencyResolver(provider.CreateScope());
public object GetService(Type serviceType) => provider.GetService(serviceType);
public IEnumerable<object> GetServices(Type type) => provider.GetServices(type);
public void Dispose() => scope?.Dispose();
}
这一实施将确保新的IServiceScope
根据每个 Web 请求创建,并且服务始终根据请求解析;不是从根源上IServiceProvider
.
尽管这可以解决您的问题,但其他实现可能仍然有益。
The IDependencyResolver
合同有问题,因为被迫退货null
当打电话给GetService
不会导致注册的正确解析。这意味着当您忘记注册控制器时,您最终会遇到这些恼人的“确保控制器具有无参数公共构造函数”错误。
因此,创建自定义的要容易得多IHttpControllerActivator
反而。在这种情况下你可以打电话GetRequiredService
这将never return null
:
public class MsDiHttpControllerActivator : IHttpControllerActivator
{
private readonly ServiceProvider provider;
public MsDiHttpControllerActivator(ServiceProvider provider) =>
this.provider = provider;
public IHttpController Create(
HttpRequestMessage request, HttpControllerDescriptor d, Type controllerType)
{
IServiceScope scope = this.provider.CreateScope();
request.RegisterForDispose(scope); // disposes scope when request ends
return (IHttpController)scope.ServiceProvider.GetRequiredService(controllerType);
}
}
This MsDiHttpControllerActivator
可以将实现添加到 Web API 管道中,如下所示:
GlobalConfiguration.Configuration.Services
.Replace(typeof(IHttpControllerActivator),
new MsDiHttpControllerActivator(services.BuildServiceProvider(true)));
这消除了需要有一个IDependencyResolver
执行。不过,您仍然需要注册控制器:
services.AddTransient<TestController>();
另请注意,我对此进行了更改:
services.BuildServiceProvider()
To this:
services.BuildServiceProvider(true)
这是一个非常重要的改变;它可以保护您(在某些方面)免受强制依赖项 https://blog.ploeh.dk/2014/06/02/captive-dependency/,这是使用 DI 容器时的主要问题之一。由于一些不为人知的原因,BuildServiceProvider()
过载默认为false
,这意味着它不会验证您的范围。