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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15908019/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 18:25:56  来源:igfitidea点击:

Simple Injector unable to inject dependencies in Web API controllers

c#asp.net-web-apidependency-injectionsimple-injector

提问by user1417835

I am attempting to do some basic constructor DI with Simple Injector, and it seems that it is unable to resolve the dependencies for Web API controllers.

我正在尝试使用 Simple Injector 执行一些基本的构造函数 DI,但它似乎无法解析 Web API 控制器的依赖关系。

  • I have an API controller in an "API" folder, that is outside the "Controllers" folder.
  • I have also tried placing it within the "Controllers" folder, but that did not seem to make much of a difference. The stack trace that I receive is similar to the one presented in this question.
  • I am using a fresh install of the "Simple Injector MVC Integration Quick Start" NuGet Package (v. 2.1.0).
  • I have the base SimpleInjectorWebApiDependencyResolverfrom the documentation, which is also the same as found here.
  • I am using Entity Framework, and have looked at the discussion threadabout changes to correctly load the context.
  • 我在“API”文件夹中有一个 API 控制器,位于“Controllers”文件夹之外。
  • 我也尝试将它放在“Controllers”文件夹中,但这似乎没有太大区别。我收到的堆栈跟踪类似于此问题中提供的堆栈跟踪。
  • 我正在使用全新安装的“Simple Injector MVC 集成快速入门”NuGet 包 (v. 2.1.0)。
  • 我有SimpleInjectorWebApiDependencyResolver文档中的基础,这也与此处找到的相同。
  • 我正在使用实体框架,并查看了有关更改以正确加载上下文的讨论线程

This does not seem to be a problem, but I still receive the following error:

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

Type 'MyProject.API.ArticleController' does not have a default constructor

System.ArgumentException at

System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)

类型“MyProject.API.ArticleController”没有默认构造函数

System.ArgumentException 在

System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType) , Func`1& activator) 在 System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)

It would be appreciated if someone could offer me some suggestions, on whether anything should be modified from its current state/call order.

如果有人可以向我提供一些关于是否应该从当前状态/调用顺序修改任何内容的建议,我将不胜感激。

ArticleController (basic structure):

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:

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:

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

采纳答案by Steven

TLTR:the problem is caused by the implicit way Web API handles resolving controller types; register your Web API controllers explicitly and you'll see where the problem is.

TLTR:问题是由 Web API 处理解析控制器类型的隐式方式引起的;显式注册您的 Web API 控制器,您将看到问题出在哪里。

Here is a step by step what is happening under the covers:

以下是幕后发生的事情的分步说明:

  1. The System.Web.Http.DefaultHttpControllerActivatorcalls into the SimpleInjectorWebApiDependencyResolverand requests the creation of an API controller.
  2. SimpleInjectorWebApiDependencyResolverforwards that call to the SimpleInjector.Containerinstance.
  3. That Containerinstance however, does not have any explicit registrations for that API Controller (since you supplied an empty container to the resolver).
  4. Since there is no explicit registration, the container tries to do a last minute registration for that type.
  5. That Controller type however depends on interfaces that can't be resolved because they are not registered in the container (remember, your container is empty).
  6. Although the container would normally throw an exception, null is returned in this case, because the type is requested through the IServiceProvider.GetServicemethod and the type was not registered explictly.
  7. The SimpleInjectorWebApiDependencyResolver's GetServicemethod will return nullas well, since it's by definition that it should return null; It should return null when no registration exists (which currently is the case).
  8. Since the DependencyResolverreturned null, DefaultHttpControllerActivatorwill fall back to its default behavior, which means creating that type itself, but this requires the controller to have a default constructor.
  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返回的 nullDefaultHttpControllerActivator将回退到其默认行为,这意味着自己创建该类型,但这需要控制器具有默认构造函数。

Long story short, the problem is caused by the implicit way Web API handles resolving controller types.

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

So the solution here is to:

所以这里的解决方案是:

  1. Have only one single Containerin your web application. This prevents all sorts of trouble and complication of your configuration.
  2. Register all Web API Controllers explicitly in the container. Registering controllers explicitly will ensure that Simple Injector will throw an exception when a controller can't be resolved. Besides, this allows you to call container.Verify()which will make the application fail during startup when the configuration is invalid (a verifiable configuration is important). And this also allows you to diagnose the configurationwhich gives you even more confidence about the correctness of your configuration.
  1. Container在您的 Web 应用程序中只有一个。这可以防止您的配置出现各种麻烦和复杂化。
  2. 在容器中显式注册所有 Web API 控制器。显式注册控制器将确保 Simple Injector 在无法解析控制器时抛出异常。此外,这允许您container.Verify()在配置无效时调用会使应用程序在启动期间失败(可验证的配置很重要)。这还允许您诊断配置,从而使您对配置的正确性更有信心。

My advice is to place MVC and Web API in their own project. This will make things much easier.

我的建议是将 MVC 和 Web API 放在他们自己的项目中。这将使事情变得容易得多。

Registering all Web API controllers can be done with the following code:

可以使用以下代码注册所有 Web API 控制器:

container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

UPDATE:

更新:

Because this error is so common, newer versions of the SimpleInjectorWebApiDependencyResolverclass will simply neverreturn nullwhen a controller type is requested. Instead it will throw a descriptive error. Because of this you should never see error anymore, as long as you use the official SimpleInjectorWebApiDependencyResolver.

由于此错误非常常见,因此在请求控制器类型时,SimpleInjectorWebApiDependencyResolver该类的较新版本将永远不会返回null。相反,它会抛出一个描述性错误。因此,只要您使用官方的SimpleInjectorWebApiDependencyResolver.

回答by Muhammad Awais

Following setups work for me:

以下设置对我有用:

1) include Unity.WebAPIfrom https://www.nuget.org/packages/Unity.WebAPI/2) in UnityConfig

1)包括Unity.WebAPIhttps://www.nuget.org/packages/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) in Global.asaxfile

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


Getting started with Unity.WebAPI

Unity.WebAPI 入门

To get started, just add a call to UnityConfig.RegisterComponents() in the Application_Start method of Global.asax.cs and the Web API framework will then use the Unity.WebAPI DependencyResolver to resolve your components.

首先,只需在 Global.asax.cs 的 Application_Start 方法中添加对 UnityConfig.RegisterComponents() 的调用,然后 Web API 框架将使用 Unity.WebAPI DependencyResolver 来解析您的组件。

e.g.

例如

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

Add your Unity registrations in the RegisterComponents method of the UnityConfig class. All components that implement IDisposable should be registered with the HierarchicalLifetimeManager to ensure that they are properly disposed at the end of the request.

在 UnityConfig 类的 RegisterComponents 方法中添加您的 Unity 注册。所有实现 IDisposable 的组件都应该向 HierarchicalLifetimeManager 注册,以确保它们在请求结束时被正确处理。