C# MVC5 Web API和依赖注入

C# MVC5 Web API和依赖注入,c#,asp.net-web-api,dependency-injection,asp.net-mvc-5,asp.net-web-api2,C#,Asp.net Web Api,Dependency Injection,Asp.net Mvc 5,Asp.net Web Api2,试图在没有第三方工具的情况下在Web API 2上进行一些DI。 因此,从一些示例中,我得到了自定义依赖项解析器(为什么没有集成的解析器?奇怪的是,甚至Microsoft.Extensions.DependencyInjection没有提供任何功能): 但当我尝试使用它时: public class TestController : ApiController { private IMyService _myService = null; public

试图在没有第三方工具的情况下在Web API 2上进行一些DI。
因此,从一些示例中,我得到了自定义依赖项解析器(为什么没有集成的解析器?奇怪的是,甚至Microsoft.Extensions.DependencyInjection没有提供任何功能):

但当我尝试使用它时:

public class TestController : ApiController
    {
        private IMyService _myService = null;

        public TestController(IMyService myService)
        {
            _myService = myService;
        }

        public void Get()
        {
            _myService.DoWork();
        }
}
我得到一个错误:

尝试创建“TestController”类型的控制器时出错。确保控制器具有无参数公共构造函数


如何以正确的方式烹调这道菜?

你所看到的一切都与此有关。简而言之,Web API将调用其默认的
IHttpControllerActivator
实现来请求新的控制器实例。该实例将调用您的
DependencyResolver.GetService
方法。该方法将调用转发到MS.DI的
GetService
方法。但是,由于您没有将控制器注册到MS.DI容器中,因此它将返回
null
。这将导致默认的IHttpControllerActivator尝试使用反射创建控制器,但这需要一个默认构造函数。由于控制器没有异常消息,因此会产生相当隐晦的异常消息

因此,快速解决方案是注册控制器,例如:

services.AddTransient<TestController>();
此实现将确保在每个web请求上创建一个新的
IServiceScope
,并始终从请求中解析服务;不是从根
IServiceProvider

虽然这将解决您的问题,但另一个实现可能仍然是有益的

idependencysolver
契约有问题,因为当调用
GetService
不能正确解析注册时,它被迫返回
null
。这意味着,当您忘记注册控制器时,将出现这些恼人的“确保控制器具有无参数公共构造函数”错误

因此,创建自定义
IHTTP控制器激活器
要容易得多。在这种情况下,您可以调用
GetRequiredService
,它将永远不会返回
null

public类MsDiHttpControllerActivator:IHttpControllerActivator
{
私有只读服务提供商;
公共MsDiHttpControllerActivator(服务提供程序)=>
this.provider=提供者;
公共IHTTP控制器创建(
HttpRequestMessage请求,HttpControllerDescriptor d,类型controllerType)
{
IServiceScope范围=this.provider.CreateScope();
request.RegisterForDispose(scope);//在请求结束时处理作用域
return(IHttpController)scope.ServiceProvider.GetRequiredService(controllerType);
}
}
MsDiHttpControllerActivator
实现可添加到Web API管道中,如下所示:

GlobalConfiguration.Configuration.Services
.更换(IHTTP控制器激活器类型),
新的MsDiHttpControllerActivator(services.BuildServiceProvider(true));
这样就不需要使用
IDependencyResolver
实现。您仍然需要注册控制器,但:

services.AddTransient<TestController>();
为此:

services.BuildServiceProvider(true)

这是一个非常重要的变化;它(在某种程度上)保护您不受影响,这是使用DI容器时的主要问题之一。出于某种模糊的原因,
BuildServiceProvider()
重载默认为
false
,这意味着它不会验证您的作用域。

在解析
TestController
时,它会中断,并显示构造函数中有关参数的相同消息。是的,我是这样做的。我在
GetService
GetServices
上设置了断点。或者你是说别的什么?不是。它有默认构造函数,不需要任何东西。谢谢您的详细回答。起初,我只是尝试将您的代码复制到project-
MsDiHttpControllerActivator
而不是自定义解析器和
GlobalConfiguration.Configuration.Services。在我的serviceConfiguration.Register中替换…
,虽然所有编译都很好,我现在得到
尚未注册“ManagementApp.Controller.TestController”类型的服务。
。也许有点误会?我正试图在WebAPI项目中使用WCF服务进行一些DI——也许这是有意义的
MyService
是一个WCF,是的。嗯,但当我添加
services.AddTransient()时,它可以工作。它看起来不像是好的干净的解决方案,所以你怎么看?我认为你应该应用自动注册(也称为组装扫描)。这样可以避免显式注册每个控制器。您可以通过对程序集进行反射来找到所有控制器类型,并将它们注册到foreach循环中来实现这一点。或者,你也可以选择成熟的、功能丰富的容器(Autofac、Simple Injector、Castle Windsor等),因为它们都包含可以为你实现这一点(以及更多)的现成功能。是的,我已经在我的
ServiceConfig
中完成了这项工作,并将其称为
services.RegisterApiControllers()
和所有工作,至少现在是这样。我还将
GlobalConfiguration.Configuration
替换为
HttpConfiguration-config
参数,该参数由Global.asax中的
GlobalConfiguration.Configuration
提供。
services.AddTransient<TestController>();
services.AddTransient<TestController>();
services.BuildServiceProvider()
services.BuildServiceProvider(true)