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方法请求的,并且类型没有显式注册。 SimpleInjectorWebApiDependencyResolverGetService 方法也将返回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?

缩小后无法解析$元素

无法从 $injector.get() 获取服务实例

错误 CS0030:无法在 Amazon Web Service 中将类型“Simple.Amazon.ECS.ImageSet[]”转换为“Simple.Amazon.ECS.ImageSet”