Simple Injector 无法在 Web API 控制器中注入依赖项
Posted
技术标签:
【中文标题】Simple Injector 无法在 Web API 控制器中注入依赖项【英文标题】:Simple Injector unable to inject dependencies in Web API controllers 【发布时间】:2013-04-01 05:48:25 【问题描述】:我正在尝试使用 Simple Injector 做一些基本的构造函数 DI,它似乎无法解析 Web API 控制器的依赖关系。
我在“API”文件夹中有一个 API 控制器,它位于“Controllers”文件夹之外。 我也尝试将它放在“Controllers”文件夹中,但是 这似乎没有太大的不同。堆栈跟踪 我收到的与this question 中的类似。 我正在使用全新安装的“Simple Injector MVC Integration Quick Start”NuGet 包 (v. 2.1.0)。 我有来自文档的基础SimpleInjectorWebApiDependencyResolver
,它也与找到的here 相同。
我正在使用实体框架,并查看了discussion
thread 关于正确加载上下文的更改。
这不 似乎是一个问题,但我仍然收到以下错误:
Type 'MyProject.API.ArticleController' 没有默认值 构造函数
System.ArgumentException 在
System.Linq.Expressions.Expression.New(类型类型)在 System.Web.Http.Internal.TypeActivator.Create[TBase](类型 实例类型)在 System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage 请求,类型控制器类型,Func`1& 激活器)在 System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage 请求,HttpControllerDescriptor 控制器描述符,类型 控制器类型)
如果有人能给我一些建议,关于是否应该从当前状态/调用顺序修改任何内容,我们将不胜感激。
ArticleController(基本结构):
public class ArticleController : ApiController
private readonly IArticleRepository articleRepository;
private readonly IUserRepository userRepository;
private readonly IReleaseRepository releaseRepository;
public ArticleController(IArticleRepository articleRepository, IUserRepository userRepository, IReleaseRepository releaseRepository)
this.articleRepository = articleRepository;
this.userRepository = userRepository;
this.releaseRepository = releaseRepository;
// GET api/Article
public IEnumerable<Article> GetArticles() // code
// GET api/Article/5
public Article GetArticle(int id) // code
// PUT api/Article/5
public HttpResponseMessage PutArticle(int id, Article article) // code
// POST api/Article
public HttpResponseMessage PostArticle(ArticleModel article) // code
// DELETE api/Article/5
public HttpResponseMessage DeleteArticle(int id) // code
SimpleInjectorInitializer:
public static class SimpleInjectorInitializer
public static void Initialize()
var container = new Container();
InitializeContainer(container);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.RegisterMvcAttributeFilterProvider();
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
private static void InitializeContainer(Container container)
container.Register<IArticleRepository, ArticleRepository>();
container.Register<IUserRepository, UserRepository>();
container.Register<IReleaseRepository, ReleaseRepository>();
Global.asax.cs:
public class WebApiApplication : System.Web.HttpApplication
private void ConfigureApi()
// Create the container as usual.
var container = new Container();
// Verify the container configuration
// container.Verify();
// Register the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
protected void Application_Start()
AreaRegistration.RegisterAllAreas();
ConfigureApi();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
【问题讨论】:
您确定ConfigureApi
方法确实在运行吗?你在里面设置断点了吗?
您为什么要为您的 Web API 配置创建一个单独的 Container
实例?
是的,我确定它已经运行。我在 GlobalConfiguration 上设置 DependencyResolver 的行上设置了一个断点。如果您的意思是在 Global.asax.cs 中,那么我正在关注本指南 (asp.net/web-api/overview/extensibility/…),看看它是否能解决我的问题。但是,它似乎没有。
【参考方案1】:
TLTR:问题是由 Web API 处理解析控制器类型的隐式方式引起的;显式注册您的 Web API 控制器,您就会发现问题出在哪里。
这里是幕后发生的一步一步:
System.Web.Http.DefaultHttpControllerActivator
调用 SimpleInjectorWebApiDependencyResolver
并请求创建 API 控制器。
SimpleInjectorWebApiDependencyResolver
将该调用转发到 SimpleInjector.Container
实例。
但是,该 Container
实例没有对该 API 控制器的任何显式注册(因为您向解析器提供了一个空容器)。
由于没有显式注册,因此容器会尝试在最后一刻为该类型进行注册。
但是,该控制器类型取决于无法解析的接口,因为它们没有在容器中注册(请记住,您的容器是空的)。
虽然容器通常会抛出异常,但在这种情况下返回null,因为类型是通过IServiceProvider.GetService
方法请求的,并且类型没有显式注册。
SimpleInjectorWebApiDependencyResolver
的GetService
方法也将返回null
,因为根据定义它应该返回null;当不存在注册时(目前就是这种情况),它应该返回 null。
由于 DependencyResolver
返回 null,DefaultHttpControllerActivator
将回退到其默认行为,这意味着自己创建该类型,但这需要控制器具有默认构造函数。
长话短说,问题是由 Web API 处理解析控制器类型的隐式方式引起的。
所以这里的解决方案是:
-
在您的 Web 应用程序中只有一个
Container
。这样可以避免配置的各种麻烦和复杂性。
在容器中显式注册所有 Web API 控制器。显式注册控制器将确保 Simple Injector 在无法解析控制器时抛出异常。此外,这允许您调用container.Verify()
,这将使应用程序在配置无效时在启动过程中失败(verifiable configuration is important)。这也允许您diagnose the configuration,这让您对配置的正确性更有信心。
我的advice is to place MVC and Web API in their own project。这会让事情变得更容易。
注册所有 Web API 控制器可以使用以下代码完成:
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
更新:
由于此错误非常常见,当请求控制器类型时,较新版本的 SimpleInjectorWebApiDependencyResolver
类将简单地从不返回 null
。相反,它会抛出一个描述性错误。因此,只要您使用官方的SimpleInjectorWebApiDependencyResolver
,您就不会再看到错误了。
【讨论】:
感谢您的详细解答!我应该把这个方法放在哪里?是否暗示我应该创建一个新类,并将其实现为 Container 的扩展方法? 这是一种扩展方法。你可以把它放在任何静态类中,这样你就可以在你的合成根中做container.RegisterApiControllers()
。
在SimpleInitializer
中设置GlobalConfiguration DependencyResolver
是否合适?还是应该将其保存在 Global.asax.cs 中?文档 (simpleinjector.codeplex.com/…) 建议容器创建和服务/资源注册应该在 Global.asax.cs 中的 Application_Start()
内完成,但 InitializeContainer()
中的 cmets 似乎也建议应该在那里进行注册。
把它放在SimpleInjectorInitiazer
中没有什么坏处。
使用 GlobalConfiguration.Configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes() 一样吗? (而不是使用您使用的 LINQ 代码搜索程序集?【参考方案2】:
以下设置对我有用:
1) 包含来自https://www.nuget.org/packages/Unity.WebAPI/ 的 Unity.WebAPI 2) 在 UnityConfig
public static class UnityConfig
public static void RegisterComponents()
var container = new UnityContainer();
// **** Important note -----
// register all your components with the container here
// e.g. container.RegisterType<ITestService, TestService>();
DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
3) 在 Global.asax 文件中
public class MvcApplication : System.Web.HttpApplication
protected void Application_Start()
AreaRegistration.RegisterAllAreas();
UnityConfig.RegisterComponents();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Unity.WebAPI 入门
要开始,只需在 Global.asax.cs 的 Application_Start 方法中添加对 UnityConfig.RegisterComponents() 的调用 然后 Web API 框架将使用 Unity.WebAPI DependencyResolver 来解析您的组件。
例如
public class WebApiApplication : System.Web.HttpApplication
protected void Application_Start()
AreaRegistration.RegisterAllAreas();
UnityConfig.RegisterComponents(); // <----- Add this line
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
在 UnityConfig 类的 RegisterComponents 方法中添加您的 Unity 注册。所有实现 IDisposable 的组件都应该是 向 HierarchicalLifetimeManager 注册,以确保在请求结束时正确处理它们。
【讨论】:
以上是关于Simple Injector 无法在 Web API 控制器中注入依赖项的主要内容,如果未能解决你的问题,请参考以下文章
使用 Quartz.NET 和 Simple Injector 进行构造函数注入
您正在尝试解决跨线服务,但在使用 Simple Injector 的活动(Async Scoped)范围之外执行此操作
如何将 Blazor 中的所有注册服务添加到 Simple Injector?
错误 CS0030:无法在 Amazon Web Service 中将类型“Simple.Amazon.ECS.ImageSet[]”转换为“Simple.Amazon.ECS.ImageSet”