简单注入器无法在Web API控制器中注入依赖项

本文关键字:控制器 注入 依赖 API Web 注入器 简单 | 更新日期: 2023-09-27 18:08:11

我试图用简单的注入器做一些基本的构造器DI,似乎无法解决Web API控制器的依赖关系。

  • 我在"API"文件夹中有一个API控制器,它在"Controllers"文件夹之外。
  • 我也试过把它放在"控制器"文件夹,但是这似乎并没有多大区别。堆栈跟踪我收到的是类似于这个问题中提出的。
  • 我正在使用一个新安装的"简单注入器MVC集成快速启动"NuGet包(v. 2.1.0)。
  • 我有基本的SimpleInjectorWebApiDependencyResolver从文档,这也是相同的,在这里找到。
  • 我正在使用实体框架,并看过讨论关于正确加载上下文的更改线程。

这不是似乎是一个问题,但我仍然收到以下错误:

MyProject.API类型"。ArticleController’没有默认值构造函数

系统。ArgumentException在

System.Linq.Expressions.Expression。新(类型类型)在System.Web.Http.Internal.TypeActivator.Create [TBase](类型instanceType)System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator (HttpRequestMessage请求,类型controllerType, Func ' 1&激活)System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (HttpRequestMessageHttpControllerDescriptor, TypecontrollerType)

如果有人能给我一些建议,关于是否需要修改当前状态/调用顺序,我将不胜感激。

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);
    }
}

简单注入器无法在Web API控制器中注入依赖项

TLTR:问题是由Web API处理解析控制器类型的隐式方式引起的;显式地注册你的Web API控制器,你会发现问题在哪里。

下面是一步步发生在被子下面的事情:

  1. System.Web.Http.DefaultHttpControllerActivator调用SimpleInjectorWebApiDependencyResolver,请求创建API控制器
  2. SimpleInjectorWebApiDependencyResolver将该调用转发给SimpleInjector.Container实例。
  3. 然而,Container实例没有为该API控制器进行任何显式注册(因为您向解析器提供了一个空容器)。
  4. 由于没有显式注册,容器尝试为该类型做最后一分钟注册。
  5. 该控制器类型取决于无法解析的接口,因为它们没有在容器中注册(记住,您的容器是空的)。
  6. 虽然容器通常会抛出异常,但在这种情况下返回null,因为该类型是通过IServiceProvider.GetService方法请求的,并且该类型没有显式注册。
  7. SimpleInjectorWebApiDependencyResolverGetService方法也将返回null,因为根据定义,它应该返回null;当不存在注册时(目前就是这种情况),它应该返回null。
  8. 由于DependencyResolver返回null, DefaultHttpControllerActivator将回到其默认行为,这意味着创建该类型本身,但这需要控制器有一个默认构造函数。

长话短说,这个问题是由Web API处理解析控制器类型的隐式方式引起的。

所以这里的解决方案是:

  1. 在你的web应用程序中只有一个Container。这可以防止各种麻烦和复杂的配置。
  2. 在容器中显式注册所有Web API控制器。显式注册控制器将确保Simple Injector在无法解析控制器时抛出异常。此外,这允许您调用container.Verify(),这将使应用程序在配置无效时启动失败(可验证的配置很重要)。这也能让你诊断构型这让你对构型的正确性更有信心。
我的建议是将MVC和Web API放在他们自己的项目中。这将使事情容易得多。

注册所有Web API控制器可以用下面的代码完成:

container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

更新:

因为这个错误很常见,所以当请求控制器类型时,新版本的SimpleInjectorWebApiDependencyResolver类将简单地永远不会返回null。相反,它将抛出一个描述性错误。因此,只要您使用官方的SimpleInjectorWebApiDependencyResolver,您就不会再看到错误了。

以下设置适合我:

1)包含Unity。WebAPI from https://www.nuget.org/packages/Unity.WebAPI/2) in 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) in 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的组件都应该是