无法使用 Unity 将依赖项注入 ASP.NET Web API 控制器

Posted

技术标签:

【中文标题】无法使用 Unity 将依赖项注入 ASP.NET Web API 控制器【英文标题】:Cannot Inject Dependencies into ASP.NET Web API Controller using Unity 【发布时间】:2012-03-20 15:25:47 【问题描述】:

有没有人成功地使用 IoC 容器将依赖项注入 ASP.NET WebAPI 控制器?我似乎无法让它工作。

这就是我现在正在做的事情。

在我的global.ascx.cs

    public static void RegisterRoutes(RouteCollection routes)
    
            // code intentionally omitted 
    

    protected void Application_Start()
    
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        IUnityContainer container = BuildUnityContainer();

        System.Web.Http.GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
            t =>
            
                try
                
                    return container.Resolve(t);
                
                catch (ResolutionFailedException)
                
                    return null;
                
            ,
            t =>
            
                try
                
                    return container.ResolveAll(t);
                
                catch (ResolutionFailedException)
                
                    return new System.Collections.Generic.List<object>();
                
            );

        System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container)); 

        BundleTable.Bundles.RegisterTemplateBundles();
    

    private static IUnityContainer BuildUnityContainer()
    
        var container = new UnityContainer().LoadConfiguration();

        return container;
    

我的控制器工厂:

public class UnityControllerFactory : DefaultControllerFactory
            
                private IUnityContainer _container;

                public UnityControllerFactory(IUnityContainer container)
                
                    _container = container;
                

                public override IController CreateController(System.Web.Routing.RequestContext requestContext,
                                                    string controllerName)
                
                    Type controllerType = base.GetControllerType(requestContext, controllerName);

                    return (IController)_container.Resolve(controllerType);
                
            

它似乎从来没有在我的统一文件中寻找解决依赖关系,我收到如下错误:

尝试创建类型控制器时出错 'PersonalShopper.Services.WebApi.Controllers.ShoppingListController'。 确保控制器有一个无参数的公共构造函数。

在 System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpControllerContext 控制器上下文,类型控制器类型) 在 System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance(HttpControllerContext 控制器上下文,HttpControllerDescriptor 控制器描述符) 在 System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateController(HttpControllerContext 控制器上下文,字符串控制器名称) 在 System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage 请求,CancellationToken 取消令牌) 在 System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage 请求,CancellationToken 取消令牌)

控制器看起来像:

public class ShoppingListController : System.Web.Http.ApiController
    
        private Repositories.IProductListRepository _ProductListRepository;


        public ShoppingListController(Repositories.IUserRepository userRepository,
            Repositories.IProductListRepository productListRepository)
        
            _ProductListRepository = productListRepository;
        

我的统一文件看起来像:

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
  <container>
    <register type="PersonalShopper.Repositories.IProductListRepository, PersonalShopper.Repositories" mapTo="PersonalShopper.Implementations.MongoRepositories.ProductListRepository, PersonalShopper.Implementations" />
  </container>
</unity>

请注意,我没有注册控制器本身,因为在以前版本的 mvc 中,控制器工厂会发现需要解决依赖关系。

似乎我的控制器工厂从未被调用过。

【问题讨论】:

【参考方案1】:

我抛出了同样的异常,在我的例子中,我在 MVC3 和 MVC4 二进制文件之间存在冲突。这阻止了我的控制器在我的 IOC 容器中正确注册。检查您的 web.config 并确保它指向正确的 MVC 版本。

【讨论】:

这解决了我的 Razor 问题,但没有解决解决 api 控制器的问题。【参考方案2】:

想通了。

对于 ApiControllers,MVC 4 使用 System.Web.Http.Dispatcher.IHttpControllerFactorySystem.Web.Http.Dispatcher.IHttpControllerActivator创建控制器。如果没有静态方法来注册这些它们的实现是什么;当它们被解析时,mvc 框架会在依赖解析器中查找实现,如果找不到,则使用默认实现。

我通过执行以下操作获得了控制器依赖项的统一解析:

创建了一个 UnityHttpControllerActivator:

public class UnityHttpControllerActivator : IHttpControllerActivator

    private IUnityContainer _container;

    public UnityHttpControllerActivator(IUnityContainer container)
    
        _container = container;
    

    public IHttpController Create(HttpControllerContext controllerContext, Type controllerType)
    
        return (IHttpController)_container.Resolve(controllerType);
    

将该控制器激活器注册为统一容器本身中的实现:

protected void Application_Start()

    // code intentionally omitted

    IUnityContainer container = BuildUnityContainer();
    container.RegisterInstance<IHttpControllerActivator>(new UnityHttpControllerActivator(container));

    ServiceResolver.SetResolver(t =>
       
         // rest of code is the same as in question above, and is omitted.
       );

【讨论】:

你是如何实现 GlobalConfiguration.Configuration.ServiceResolver.SetResolver() 的 lamda 的? 这篇文章已经过时了。请参阅 CodeKata 的回复,了解使用 MVC RC 的更清洁的解决方案。 这已经过时了。 Microsoft 有一个使用 Web API 进行 DI 的 Nuget 包。看我的回答。【参考方案3】:

您可能想看看 Unity.WebApi NuGet 包,它将整理所有这些并满足 IDisposable 组件的需求。

http://nuget.org/packages/Unity.WebAPI

http://www.devtrends.co.uk/blog/introducing-the-unity.webapi-nuget-package

【讨论】:

这里有一篇很好的博文 (netmvc.blogspot.com/2012/04/…),其中介绍了使用 Unity.mvc3(也适用于 MVC4)和 Unit.WebApi 的组合来非常干净地实现 DI。跨度> 评论中提到的链接反映了更新的 API,对于现在遇到此问题的任何人:GlobalConfiguration.Configuration.ServiceResolver.SetResolver(new Unity.WebApi.UnityDependencyResolver(container));现在是 GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container); 请注意,它还提供比自定义 UnityResolver 更好的调试消息。【参考方案4】:

这里有一个更好的解决方案可以正常工作

http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver

【讨论】:

这是一个使用 MVC RC 的更好的解决方案。您不需要提供 IHttpControllerActivator 的实现。您改为实现 System.Web.Http.Dependencies.IDependencyResolver。 不太喜欢链接到博客 - 你能在这里总结一下并放上链接吗? :) 这看起来是一个不错的方法,但我遇到了几个错误,如下所示。看起来新的解析器无法解析多种类型。依赖项解析失败,type = "System.Web.Http.Hosting.IHostBufferPolicySelector",name = "(none)"。异常发生时:解决时。例外是: InvalidOperationException - IHostBufferPolicySelector 类型没有可访问的构造函数。 --- 异常时,容器为:Resolving System.Web.Http.Hosting.IHostBufferPolicySelector,( 微软有一个 Nuget 包,它使用 Web API 进行 DI。看我的回答。【参考方案5】:

在最近的一次 RC 中,我发现不再有 SetResolver 方法。为了同时为控制器和 webapi 启用 IoC,我使用 Unity.WebApi (NuGet) 和以下代码:

public static class Bootstrapper

    public static void Initialise()
    
        var container = BuildUnityContainer();

        GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);

        ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new ControllerActivator()));
    

    private static IUnityContainer BuildUnityContainer()
    
        var container = new UnityContainer();

        container.Configure(c => c.Scan(scan =>
        
            scan.AssembliesInBaseDirectory();
            scan.With<UnityConfiguration.FirstInterfaceConvention>().IgnoreInterfacesOnBaseTypes();
        ));

        return container;
    


public class ControllerActivator : IControllerActivator

    IController IControllerActivator.Create(RequestContext requestContext, Type controllerType)
    
        return GlobalConfiguration.Configuration.DependencyResolver.GetService(controllerType) as IController;
    

我还将 UnityConfiguration(也来自 NuGet)用于 IoC 魔术。 ;)

【讨论】:

使用requestContext.Configuration.DependencyResolver.GetService()不是更好吗?【参考方案6】:

阅读答案后,我仍然需要做很多挖掘才能找到这个问题,所以这里是为了同行的利益: 这就是您在 ASP.NET 4 Web API RC 中需要做的所有事情(截至 2013 年 8 月 8 日):

    添加对“Microsoft.Practices.Unity.dll”的引用 [我正在使用版本 3.0.0.0,通过 NuGet 添加] 添加对“Unity.WebApi.dll”的引用[我使用的是 0.10.0.0 版,通过 NuGet 添加] 使用容器注册您的类型映射 - 类似于 Unity.WebApi 项目添加到您的项目中的 Bootstrapper.cs 中的代码。 在从 ApiController 类继承的控制器中,创建参数化构造函数,将其参数类型作为映射类型

您瞧,您无需另一行代码即可将依赖项注入到构造函数中!

注意:我从作者 THIS 博客中的一位 cmets 获得此信息。

【讨论】:

【参考方案7】:

我在使用 Unity.WebAPI NuGet 包时遇到了同样的问题。问题是该包从未在我的 Global.asax 中添加对 UnityConfig.RegisterComponents() 的调用。

Global.asax.cs 应如下所示:

public class WebApiApplication : System.Web.HttpApplication

    protected void Application_Start()
    
        UnityConfig.RegisterComponents();
        ...
    

【讨论】:

【参考方案8】:

由于向 Unity 注册控制器,会出现此问题。我通过使用约定注册解决了这个问题,如下所示。请根据需要过滤掉任何其他类型。

    IUnityContainer container = new UnityContainer();

    // Do not register ApiControllers with Unity.
    List<Type> typesOtherThanApiControllers
        = AllClasses.FromLoadedAssemblies()
            .Where(type => (null != type.BaseType)
                           && (type.BaseType != typeof (ApiController))).ToList();

    container.RegisterTypes(
        typesOtherThanApiControllers,
        WithMappings.FromMatchingInterface,
        WithName.Default,
        WithLifetime.ContainerControlled);

上面的例子也使用了AllClasses.FromLoadedAssemblies()。如果您正在查看从基本路径加载程序集,它可能无法在使用 Unity 的 Web API 项目中按预期工作。请查看我对与此相关的另一个问题的回答。 https://***.com/a/26624602/1350747

【讨论】:

【参考方案9】:

微软为此创建了一个包。

从包管理器控制台运行以下命令。

安装包 Unity.AspNet.WebApi

如果您已经安装了 Unity,它会询问您是否要覆盖 App_Start\UnityConfig.cs。回答否并继续。

无需更改任何其他代码,DI(具有统一性)即可工作。

【讨论】:

你能告诉我为什么当 nuget 提示覆盖 UnityConfig.cs 时我们回答“否”吗?问题2:有没有使用Unity.AspNet.WebApi的示例代码? 抱歉,我已更改为 Autofac,因为我需要一些 Unity 无法轻松完成的事情,而 Autofac 开箱即用。但是从记忆中你需要说不,如果你已经安装了统一,因为你不想覆盖你已经工作的统一配置,你只想添加统一的 webapi 配置。在 webapi 中使用 unity 正是您正常使用它的方式。如果它没有像其他类那样注入您的依赖项,我建议发布一个问题。注入 webapi 的方式与其他类(如控制器)没有什么不同 @garethb:我正在使用 unity.webapi,但必须在单元测试中添加几行代码来解析我不喜欢的 ControllerContext。假设,如果我使用 Unity.AspNet.WebApi,该地址会自动从 UnitTests 项目中解析 ControllerContext 吗?请建议 我不这么认为。很确定两者都以类似的方式工作。我在单元测试中创建了一个新的模拟上下文。 @garethb:感谢您的快速回复。嘲笑奏效了。我无法使用 Unity.WebApi 或 Unity.Aspnet.webapi 注入 UrlHelper。你还记得你在使用 Unity 时是怎么做的吗?提前谢谢你【参考方案10】:

我遇到了同样的错误,并在互联网上搜索了几个小时的解决方案。最终,我似乎必须在调用 WebApiConfig.Register 之前注册 Unity。我的 global.asax 现在看起来像

public class WebApiApplication : System.Web.HttpApplication

   protected void Application_Start()
   
       UnityConfig.RegisterComponents();
       AreaRegistration.RegisterAllAreas();
       GlobalConfiguration.Configure(WebApiConfig.Register);
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
       RouteConfig.RegisterRoutes(RouteTable.Routes);
       BundleConfig.RegisterBundles(BundleTable.Bundles);
   

对我来说,这解决了 Unity 无法解决我的控制器中的依赖项的问题

【讨论】:

【参考方案11】:

ASP.NET Web API 2 的简短摘要。

从 NuGet 安装 Unity

创建一个名为UnityResolver的新类:

using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;

public class UnityResolver : IDependencyResolver

    protected IUnityContainer container;

    public UnityResolver(IUnityContainer container)
    
        if (container == null)
        
            throw new ArgumentNullException("container");
        
        this.container = container;
    

    public object GetService(Type serviceType)
    
        try
        
            return container.Resolve(serviceType);
        
        catch (ResolutionFailedException)
        
            return null;
        
    

    public IEnumerable<object> GetServices(Type serviceType)
    
        try
        
            return container.ResolveAll(serviceType);
        
        catch (ResolutionFailedException)
        
            return new List<object>();
        
    

    public IDependencyScope BeginScope()
    
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    

    public void Dispose()
    
        Dispose(true);
    

    protected virtual void Dispose(bool disposing)
    
        container.Dispose();
    

创建一个名为UnityConfig的新类:

public static class UnityConfig

    public static void ConfigureUnity(HttpConfiguration config)
    
        var container = new UnityContainer();
        container.RegisterType<ISomethingRepository, SomethingRepository>();
        config.DependencyResolver = new UnityResolver(container);
    

编辑 App_Start -> WebApiConfig.cs

public static void Register(HttpConfiguration config)

    UnityConfig.ConfigureUnity(config);
    ...

现在它可以工作了。

原始来源,但稍作修改: https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection

【讨论】:

以上是关于无法使用 Unity 将依赖项注入 ASP.NET Web API 控制器的主要内容,如果未能解决你的问题,请参考以下文章

csharp 将Castle Windsor依赖项注入ASP.NET Web API过滤器C#

ASP.NET Core学习——6

ASP.NET Core学习——6

在 WCF 中使用 Unity 将依赖项注入到属性中

Azure webjobs - Unity - 如何将范围内的依赖项注入其他类

ASP.NET Core使用编译时依赖关系注入(DI)