Unity를 사용하여 ASP.NET Web API 컨트롤러에 종속성을 삽입 할 수 없음


82

ASP.NET WebAPI 컨트롤러에 종속성을 주입하기 위해 IoC 컨테이너를 사용하여 성공한 사람이 있습니까? 나는 그것을 작동시킬 수없는 것 같습니다.

이것이 제가 지금하고있는 일입니다.

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, Type controllerType) at System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance (HttpControllerContext controllerContext, HttpControllerDescriptor controllerDescriptor) at System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateController (HttpControllerContext controllerContext, String controllerName) at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal (HttpRequestMessage request, CancellationToken cancelToken) at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync (HttpRequestMessage request, CancellationToken cancelToken)

컨트롤러는 다음과 같습니다.

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


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

내 Unity 파일은 다음과 같습니다.

<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에서는 컨트롤러 팩토리가 종속성을 해결해야한다는 것을 파악했기 때문에 컨트롤러 자체에 대한 등록이 없습니다.

내 컨트롤러 공장이 호출되지 않는 것 같습니다.

답변:


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

해당 컨트롤러 활성화기를 Unity 컨테이너 자체의 구현으로 등록했습니다.

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()어떻게 구현 했 습니까?
jrummell

7
이 게시물은 오래되었습니다. MVC RC를 사용한보다 깨끗한 솔루션은 CodeKata의 답변을 참조하십시오.
Matt Randle

이것은 구식입니다. Microsoft에는 웹 API로 DI를 수행하는 Nuget 패키지가 있습니다. 내 대답을 참조하십시오.
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

22
블로그 링크의 열렬한 팬이 아닙니다. 여기에 요약하고 링크를 추가 할 수 있습니까? :)
Nate-Wilkins 2014

3
좋은 접근 방식처럼 보이지만 다음과 같은 몇 가지 오류가 발생합니다. 새 해결 프로그램이 여러 유형을 해결할 수없는 것 같습니다. 종속성 확인에 실패했습니다. 유형 = "System.Web.Http.Hosting.IHostBufferPolicySelector", 이름 = "(없음)". 해결 중에 예외가 발생했습니다. 예외 : InvalidOperationException-IHostBufferPolicySelector 유형에 액세스 가능한 생성자가 없습니다. --- 예외 발생 당시 컨테이너는 다음과 같습니다. Resolving System.Web.Http.Hosting.IHostBufferPolicySelector, (
ATHER

Microsoft에는 웹 API로 DI를 수행하는 Nuget 패키지가 있습니다. 내 대답을 참조하십시오.
garethb

24

이 모든 것을 분류하고 IDisposable 구성 요소를 제공하는 Unity.WebApi NuGet 패키지를 살펴볼 수 있습니다.

보다

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

또는

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


2
여기에는 Unity.mvc3 (MVC4에서도 작동) 및 Unit.WebApi의 조합을 사용하여 DI를 매우 깔끔하게 구현 하는 방법이있는 좋은 블로그 게시물 ( netmvc.blogspot.com/2012/04/… )이 있습니다.
Phred Menyhert 2012-08-25

1
주석에 언급 된 링크는 현재이 문제를 접하는 모든 사용자를 위해 업데이트 된 API를 반영합니다. GlobalConfiguration.Configuration.ServiceResolver.SetResolver (new Unity.WebApi.UnityDependencyResolver (container)); 이제 GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver (container);
Robert Zahm

또한 사용자 지정 UnityResolver.
Ioannis Karadimas 2015 년

9

Microsoft는이를위한 패키지를 만들었습니다.

패키지 관리자 콘솔에서 다음 명령을 실행하십시오.

설치 패키지 Unity.AspNet.WebApi

Unity가 이미 설치되어있는 경우 App_Start \ UnityConfig.cs를 덮어 쓸지 묻는 메시지가 표시됩니다. 아니오라고 대답하고 계속하십시오.

다른 코드를 변경할 필요가 없으며 DI (통합 포함)가 작동합니다.


너겟이 UnityConfig.cs를 덮어 쓰라는 메시지를 표시 할 때 왜 '아니요'라고 대답하는지 알려주시겠습니까? 질문 2 : Unity.AspNet.WebApi를 사용하기위한 샘플 코드가 있습니까?
Thomas.Benz

죄송합니다. Unity가 쉽게 수행하지 않고 Autofac이 즉시 수행해야하는 작업이 있었기 때문에 Autofac로 변경했습니다. 그러나 메모리에서 이미 작동하는 unity 구성을 덮어 쓰고 싶지 않기 때문에 이미 unity가 설치되어 있다면 no라고 말해야합니다. yuo는 unity webapi 구성 만 추가하고 싶습니다. webapi에서 unity를 사용하는 것은 정상적으로 사용하는 방법입니다. 다른 클래스와 마찬가지로 종속성을 주입하지 않으면 질문을 게시하는 것이 좋습니다. (컨트롤러와 같은) 다른 클래스에 비해 webapi에 주입의 어떤 다른 방법이 없다
garethb을

@garethb : unity.webapi를 사용하고 있지만 내가 좋아하지 않는 ControllerContext를 해결하기 위해 단위 테스트에서 몇 줄의 코드를 추가해야합니다. Unity.AspNet.WebApi를 사용하는 경우 해당 주소가 UnitTests 프로젝트 내에서 ControllerContext를 자동으로 해결한다고 가정 해 보겠습니다. 제안 해주세요
sam

나는 그렇게 생각하지 않는다. 둘 다 비슷한 방식으로 작동한다고 확신합니다. 내 단위 테스트에서 새로운 모의 컨텍스트를 만듭니다.
garethb

@garethb : 빠른 응답에 감사드립니다. 조롱이 작동했습니다. Unity.WebApi 또는 Unity.Aspnet.webapi를 사용하여 UrlHelper를 삽입 할 수 없습니다. Unity를 사용할 때 어떻게했는지 기억하십니까? 미리 감사드립니다
sam

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

또한 IoC 마술을 위해 UnityConfiguration (NuGet에서도 제공)을 사용합니다. ;)


requestContext.Configuration.DependencyResolver.GetService ()를 사용하는 것이 더 낫지 않을까요?
Alwyn 2013 년

2

답변을 읽은 후에도 여전히이 문제를 해결하기 위해 많은 작업을 수행해야했기 때문에 동료의 이익을 위해 여기에 있습니다. ASP.NET 4 Web API RC에서 수행해야 할 모든 작업입니다 (8 월 8 일 기준). '13) :

  1. "Microsoft.Practices.Unity.dll"에 대한 참조 추가 [NuGet을 통해 추가 된 버전 3.0.0.0에 있습니다.]
  2. "Unity.WebApi.dll"에 대한 참조 추가 [NuGet을 통해 추가 된 버전 0.10.0.0에 있습니다.]
  3. Unity.WebApi 프로젝트에서 프로젝트에 추가 한 Bootstrapper.cs의 코드와 유사하게 컨테이너에 유형 매핑을 등록합니다.
  4. ApiController 클래스에서 상속하는 컨트롤러에서 매개 변수 유형을 매핑 된 유형으로 갖는 매개 변수화 된 생성자를 만듭니다.

자, 보라, 다른 코드 줄없이 생성자에 의존성을 주입 할 수 있습니다!

참고 : 블로그의 작성자가 작성한 댓글 중 하나에서이 정보를 얻었습니다 .


2

ASP.NET Web API 2에 대한 간략한 요약입니다.

UnityNuGet에서 설치합니다 .

라는 새 클래스를 만듭니다 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


1

동일한 예외가 발생했으며 제 경우에는 MVC3와 MVC4 바이너리간에 충돌이있었습니다. 이로 인해 컨트롤러가 IOC 컨테이너에 제대로 등록되지 않았습니다. web.config를 확인하고 올바른 버전의 MVC를 가리키고 있는지 확인하십시오.


이것은 내 Razor 문제를 해결했지만 API 컨트롤러 해결 문제는 해결하지 못했습니다.
Oved D

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();
        ...
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.