Unityを使用してASP.NETWebAPIコントローラーに依存関係を挿入できません


82

IoCコンテナーを使用してASP.NETWebAPIコントローラーに依存関係を注入することに成功した人はいますか?私はそれを機能させることができないようです。

これが私が今していることです。

私の中で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 2012年

7
この投稿は古くなっています。MVC RCを使用したよりクリーンなソリューションについては、CodeKataによる返信を参照してください。
マットランドル2012年

これは時代遅れです。Microsoftには、WebAPIでDIを実行するNugetパッケージがあります。私の答えを見てください。
garethb 2015

37

ここで正しく機能するより良い解決策があります

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


1
これは、MVCRCを使用したはるかに優れたソリューションです。IHttpControllerActivatorの実装を提供する必要はありません。代わりに、System.Web.Http.Dependencies.IDependencyResolverを実装します。
マットランドル2012年

22
ブログへのリンクの大ファンではありません-ここに要約してリンクを貼っていただけますか?:)
Nate-Wilkins

3
良いアプローチのように見えますが、次のようなエラーがいくつか発生します。新しいリゾルバーはいくつかのタイプを解決できないようです。依存関係の解決に失敗しました。type= "System.Web.Http.Hosting.IHostBufferPolicySelector"、name = "(none)"。例外が発生しました:解決中。例外は次のとおりです。InvalidOperationException-タイプIHostBufferPolicySelectorには、アクセス可能なコンストラクターがありません。---例外の時点で、コンテナは次のとおりでした:Resolving System.Web.Http.Hosting.IHostBufferPolicySelector、(
ATHER

Microsoftには、WebAPIでDIを実行するNugetパッケージがあります。私の答えを見てください。
garethb 2015

24

Unity.WebApi NuGetパッケージを確認することをお勧めします。このパッケージは、これらすべてを整理し、IDisposableコンポーネントにも対応します。

見る

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

または

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


2
Unity.mvc3MVC4でも動作します)とUnit.WebApiの組み合わせを使用してDIをかなりきれいに実装するためのウォークスルーが記載された、優れたブログ投稿(netmvc.blogspot.com/2012/04/…)があります。
Phred Menyhert 2012

1
コメントに記載されているリンクは、これに遭遇したすべての人のために、更新されたAPIを反映しています。GlobalConfiguration.Configuration.ServiceResolver.SetResolver(new Unity.WebApi.UnityDependencyResolver(container)); 現在はGlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
Robert Zahm 2013年

また、カスタムよりも優れたデバッグメッセージを提供することに注意してくださいUnityResolver
ioannis Karadimas 2015

9

Microsoftは、このためのパッケージを作成しました。

パッケージマネージャーコンソールから次のコマンドを実行します。

インストールパッケージUnity.AspNet.WebApi

Unityがすでにインストールされている場合は、App_Start \ UnityConfig.csを上書きするかどうかを尋ねられます。いいえと答えて続行します。

他のコードを変更する必要はなく、DI(ユニティ付き)が機能します。


nugetがUnityConfig.csを上書きするように要求したときに、なぜ「いいえ」と答えるのか教えていただけますか?質問2:Unity.AspNet.WebApiを使用するためのサンプルコードはありますか?
Thomas.Benz 2015年

申し訳ありませんが、Unityで簡単に実行できず、Autofacですぐに実行できる必要があることがいくつかあったため、Autofacに変更しました。しかし、すでに動作しているUnity構成を上書きしたくないので、すでにUnityがインストールされている場合は、メモリから「いいえ」と言う必要があります。yuoはUnityWebapi構成のみを追加します。webapiでUnityを使用することは、通常の使用方法とまったく同じです。他のクラスのように依存関係が注入されない場合は、質問を投稿することをお勧めします。他のクラス(コントローラーなど)と同じようにwebapiに注入する方法はありません
garethb 2015年

@garethb:私はunity.webapiを使用していますが、ユニットテストで数行のコードを追加して、気に入らないControllerContextを解決する必要があります。Unity.AspNet.WebApiを使用する場合、そのアドレスはUnitTestsプロジェクト内からControllerContextを自動的に解決するとしますか?提案してください
sam

私はそうは思いません。どちらも同じように機能することは間違いありません。単体テストで新しいモックコンテキストを作成します。
garethb 2016

@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()を使用する方が良いのではないでしょうか。
アルウィン2013年

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クラスから継承するコントローラーで、マップされたタイプとしてパラメータータイプを持つパラメーター化されたコンストラクターを作成します

見よ、別のコード行なしで依存関係がコンストラクターに注入されます!

注:この情報は、このブログの作成者によるコメントの1つから取得しました。


2

ASP.NET Web API2の簡単な要約。

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 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を使用するWebAPIプロジェクトでは期待どおりに機能しない可能性があります。これに関連する別の質問に対する私の回答を見てください。https://stackoverflow.com/a/26624602/1350747


0

Unity.WebAPINuGetパッケージを使用したときに同じ問題が発生しました。問題は、パッケージがへの呼び出しを追加しなかったことでした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.