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

时间:2012-03-02 04:29:46

标签: c# .net asp.net-web-api unity-container

是否有人使用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);
                }
            }

我似乎永远不会在我的Unity文件中查找解析依赖项,并且我收到如下错误:

  

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

     

在   System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpControllerContext   controllerContext,类型controllerType)        在System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance(HttpControllerContext)   controllerContext,HttpControllerDescriptor controllerDescriptor)        在System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateController(HttpControllerContext)   controllerContext,String controllerName)        在System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage)   请求,取消语音取消语句)        在System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage)   请求,取消语音取消语言)

控制器看起来像:

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

好像我的控制器工厂从未被调用过。

11 个答案:

答案 0 :(得分:42)

想出来。

对于 ApiControllers ,MVC 4使用 System.Web.Http.Dispatcher.IHttpControllerFactory System.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.
       });
}

答案 1 :(得分:37)

答案 2 :(得分:22)

您可能需要查看Unity.WebApi NuGet包,它将对所有这些进行排序,并且还可以满足IDisposable组件的需要。

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

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

答案 3 :(得分:9)

Microsoft为此创建了一个包。

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

install-package Unity.AspNet.WebApi

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

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

答案 4 :(得分: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无法解决控制器中依赖关系的问题

答案 5 :(得分:2)

在最近的RC中,我发现不再有SetResolver方法。要启用IoC for controller和webapi,我使用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魔术。 ;)

答案 6 :(得分:2)

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

  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类继承的控制器中,创建将参数类型作为映射类型的参数化构造函数
  5. 瞧,您可以在没有其他代码行的情况下将依赖项注入构造函数中!

    注意:我从其作者的THIS博客中的一条评论中获得了此信息。

答案 7 :(得分:1)

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

答案 8 :(得分: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

答案 9 :(得分:0)

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

Global.asax.cs应如下所示:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        UnityConfig.RegisterComponents();
        ...
    }
}

答案 10 :(得分:0)

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 - &gt; WebApiConfig.cs

public static void Register(HttpConfiguration config)
{
    UnityConfig.ConfigureUnity(config);
    ...

现在它会起作用。

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