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


82

使用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.DefaultHttpControllerFactory.CreateInstance(在System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateController)处(System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance(在HttpControllerContext controllerContext,HttpControllerDescriptor controllerDescriptor) System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage请求,CancellationToken cancelToken)处的(HttpControllerContext controllerContext,String controllerName)在System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage请求,CancellationToken cancelToken)处的(HttpControllerContext controllerContext,String controllerName)

控制器看起来像:

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中,控制器工厂会指出需要解决依赖关系。

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

Answers:


42

弄清楚了。

对于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.
       });
}

您是如何实现lamda的GlobalConfiguration.Configuration.ServiceResolver.SetResolver()
jrummell 2012年

7
该帖子已过期。有关使用MVC RC的更干净的解决方案,请参见CodeKata的答复。
Matt Randle 2012年

这已经过时了。微软有一个Nuget软件包,可以通过Web API进行DI。看我的答案。
garethb

37

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

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


1
这是使用MVC RC更好的解决方案。您不需要提供IHttpControllerActivator的实现。您改为实现System.Web.Http.Dependencies.IDependencyResolver。
Matt Randle 2012年

22
并不是链接到博客的忠实拥护者-您可以在这里总结一下并放置链接吗?:)
Nate-Wilkins 2014年

3
看来这是一种好方法,但我遇到了以下类似的错误。看起来新的解析器无法解析几种类型。解决依赖关系失败,键入=“ System.Web.Http.Hosting.IHostBufferPolicySelector”,名称=“(无)”。发生以下异常:正在解决。异常是:InvalidOperationException-IHostBufferPolicySelector类型没有可访问的构造函数。---在发生异常时,容器为:解析System.Web.Http.Hosting.IHostBufferPolicySelector,(
ATHER

微软有一个Nuget软件包,可以通过Web API进行DI。看我的答案。
garethb

24

您可能想看一下Unity.WebApi NuGet软件包,该软件包将对所有这些进行整理,同时还可满足IDisposable组件的需求。

看到

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

要么

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


2
这里有一篇不错的博客文章(netmvc.blogspot.com/2012/04/…),其中演练了结合使用Unity.mvc3(也可以与MVC4一起使用)和Unit.WebApi完美地实现DI的组合。
Phred Menyhert,2012年

1
注释中提到的链接针对现在遇到的任何人反映了更新的API:GlobalConfiguration.Configuration.ServiceResolver.SetResolver(new Unity.WebApi.UnityDependencyResolver(container)); 现在是GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
罗伯特·扎姆

请注意,与custom相比,它还提供了更好的调试消息UnityResolver
Ioannis Karadimas

9

Microsoft为此创建了一个程序包。

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

安装软件包Unity.AspNet.WebApi

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

无需更改任何其他代码,DI(统一)将起作用。


您能告诉我为什么当nuget提示覆盖UnityConfig.cs时我们回答“否”吗?问题2:是否有一些示例代码可以使用Unity.AspNet.WebApi?
Thomas.Benz 2015年

抱歉,我已经更改为Autofac,因为有些事情使Unity无法轻松完成,而Autofac开箱即用。但是从内存中,如果您已经安装了unity,则您需要拒绝,因为您不想覆盖已经工作的unity config,您只想添加unity webapi配置。在webapi中使用统一性正是您通常使用它的方式。如果它没有像其他类那样注入您的依赖关系,建议您提出一个问题。注入webapi的方式与其他类(例如控制器)没有不同的方法
garethb 2015年

@garethb:我正在使用unity.webapi,但必须在单元测试中添加几行代码来解决我不喜欢的ControllerContext。假设,如果我使用Unity.AspNet.WebApi,该地址是否可以从UnitTests项目中自动解析ControllerContext?请提出建议
萨姆

我不这么认为。可以肯定的是,两者的工作方式相似。我在单元测试中创建了一个新的模拟上下文。
garethb '16

@garethb:谢谢您的快速回复。嘲笑有效。我无法使用Unity.WebApi或Unity.Aspnet.webapi注入UrlHelper。您还记得使用Unity时的做法吗?在此先感谢您
萨姆

3

我遇到了同样的错误,并在互联网上搜索了几个小时的解决方案。最终,似乎我必须在调用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无法解析控制器中的依赖项的问题


2

在最近的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()会更好吗?
Alwyn

2

阅读答案之后,我仍然需要进行很多挖掘工作才能着手解决这个难题,因此在这里,这是对同行的好处:这是您在ASP.NET 4 Web API RC中需要做的(截至8月8日) '13):

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

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

注:我从一个评论此信息THIS它的作者博客。


2

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

Unity从NuGet安装。

创建一个名为的新类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/zh-cn/aspnet/web-api/overview/advanced/dependency-injection


1

我抛出了相同的异常,就我而言,我在MVC3和MVC4二进制文件之间发生冲突。这使我的控制器无法在IOC容器中正确注册。检查您的web.config并确保它指向正确的MVC版本。


这解决了我的Razor问题,但没有解决解决api控制器的问题。
Oved D 2012年

1

由于在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://stackoverflow.com/a/26624602/1350747


0

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

Global.asax.cs应该看起来像这样:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        UnityConfig.RegisterComponents();
        ...
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.