web-dev-qa-db-ja.com

Unityを使用してASP.NET Web API Controllerに依存関係を挿入できません

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 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)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 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の以前のバージョンでは、コントローラーファクトリが依存関係を解決する必要があると判断するため、コントローラー自体の登録がないことに注意してください。

私のコントローラーファクトリーが呼び出されることはないようです。

81
Oved D

理解した。

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.
       });
}
42
Oved D

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

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

37
CodeKata

このすべてを整理し、IDisposableコンポーネントに対応するUnity.WebApi NuGetパッケージをご覧ください。

見る

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

または

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

22
Paul Hiles

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

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

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

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

他のコードを変更する必要はなく、DI(単一性)が機能します。

9
garethb

私は同じエラーがあり、数時間インターネットで解決策を探していました。最終的に、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がコントローラーの依存関係を解決できないという問題を解決しました

3
Vincent

答えを読んだ後、私はまだこの落とし穴に着陸するために多くのことをしなければならなかったので、ここではピアの利益のためです:これはASP.NET 4 Web API RCで行う必要があるすべてです(8月8日現在) '13):

  1. 「Microsoft.Practices.Unity.dll」への参照を追加します[バージョン3.0.0.0で、NuGetを使用して追加しました]
  2. 「Unity.WebApi.dll」への参照を追加します[NuGetで追加されたバージョン0.10.0.0です]
  3. タイプマッピングをコンテナに登録します-Unity.WebApiプロジェクトによってプロジェクトに追加されるBootstrapper.csのコードに似ています。
  4. ApiControllerクラスから継承するコントローラーで、マップされたタイプとしてパラメータータイプを持つパラメーター化されたコンストラクターを作成します。

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

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

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からも)を使用します。 ;)

2
Noogen

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

1
Ogglas

同じ例外がスローされ、私の場合、MVC3バイナリとMVC4バイナリの間で競合が発生しました。これにより、コントローラーがIOCコンテナーに正しく登録されませんでした。 web.configをチェックし、MVCの正しいバージョンを指していることを確認してください。

1
user1233012

この問題は、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

1

Unity.WebAPI NuGetパッケージを使用するときに同じ問題が発生しました。問題は、パッケージがGlobal.asaxでUnityConfig.RegisterComponents()への呼び出しを追加しないことでした。

Global.asax.csは次のようになります。

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