C# MVC5 Web API和依赖注入
试图在没有第三方工具的情况下在Web API 2上进行一些DI。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
因此,从一些示例中,我得到了自定义依赖项解析器(为什么没有集成的解析器?奇怪的是,甚至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)