web-dev-qa-db-ja.com

Castle Windsorを使用したWeb APIでの依存性注入

Castle Windsorを使用してWebApiアプリケーションに依存性注入を実装したい。私は次のサンプルコードを持っています-

インターフェース-

public interface iWatch
{
    {
        DateTime GetTime();
    }
}

以下ウォッチクラス実装iWatchインターフェース-

public class Watch:iWatch
{
        public DateTime GetTime()
        {
            return DateTime.Now;
        }
}

WebApi Controller-WatchController以下の通り-

public class WatchController : ApiController
{
        private readonly iWatch _watch;

        public WatchController()
        {
            _watch = new Watch();
        }

        //http://localhost:48036/api/Watch
        public string Get()
        {
            var message = string.Format("The current time on the server is: {0}", _watch.GetTime());
            return message;
        }
}

現在、私はWatchControllerコンストラクターのWatchでiWatchオブジェクトを開始しています。 Windsor Castleの依存性注入原理を使用して、コンストラクター内でiWatchを初期化する依存性を削除したいと思います。

誰かがこのWebApiの場合に依存性注入を実装する手順を教えてくれますか?前もって感謝します!

21
Sumit Deshpande

CodeCaster、Noctis、Cristianoは、すべてのヘルプとガイダンスに感謝します。上記のクエリに対する解決策を取得しました-

最初のステップは、nugetを使用してWindsor.Castleパッケージを WebApi ソリューションにインストールすることです。

enter image description here

次のコードスニペットを検討してください-

インターフェースiWatch.cs

public interface iWatch
{
     DateTime GetTime();
}

クラスWatch.cs

public class Watch:iWatch
{
    public DateTime GetTime()
    {
        return DateTime.Now;
    }
}

ApiControllerWatchController.csは次のように定義されます:-

public class WatchController : ApiController
{
     private readonly iWatch _watch;

     public WatchController(iWatch watch)
     {
         _watch = watch;
     }

     public string Get()
     {
         var message = string.Format("The current time on the server is: {0}", _watch.GetTime());
         return message;
     }
}

コントローラーでは、WatchControllerコンストラクターのiWatchオブジェクトを通じて依存関係を注入しました。 IDependencyResolverIDependencyScope を使用して、Web APIで依存関係の注入を実現しました。 IDependencyResolverインターフェイスは、リクエストスコープ外のすべてを解決するために使用されます。

WindsorDependencyResolver.cs

internal sealed class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer _container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        _container = container;
    }
    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(_container);
    }

    public void Dispose()
    {

    }
}

WindsorDependencyScope.cs

internal sealed class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer _container;
    private readonly IDisposable _scope;

    public WindsorDependencyScope(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
        _scope = container.BeginScope();
    }

    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }
}

WatchInstaller.cs

インストーラーは、単にIWindsorInstallerインターフェイスを実装するタイプです。インターフェースには、Installと呼ばれる単一のメソッドがあります。このメソッドはコンテナのインスタンスを取得します。次に、Fluent Registration APIを使用してコンポーネントを登録できます。

public class WatchInstaller : IWindsorInstaller
{
      public void Install(IWindsorContainer container, IConfigurationStore store)
      {
      //Need to Register controllers explicitly in your container
      //Failing to do so Will receive Exception:

      //> An error occurred when trying to create //a controller of type
      //> 'xxxxController'. Make sure that the controller has a parameterless
      //> public constructor.

      //Reason::Basically, what happened is that you didn't register your controllers explicitly in your container. 
      //Windsor tries to resolve unregistered concrete types for you, but because it can't resolve it (caused by an error in your configuration), it return null.
      //It is forced to return null, because Web API forces it to do so due to the IDependencyResolver contract. 
      //Since Windsor returns null, Web API will try to create the controller itself, but since it doesn't have a default constructor it will throw the "Make sure that the controller has a parameterless public constructor" exception.
      //This exception message is misleading and doesn't explain the real cause.

      container.Register(Classes.FromThisAssembly()
                            .BasedOn<IHttpController>()
                            .LifestylePerWebRequest());***
          container.Register(
              Component.For<iWatch>().ImplementedBy<Watch>()
          );
      }
}

最後に、デフォルトの依存関係リゾルバーをGlobal.asax.cs(Application_Startメソッド)のWindsor実装に置き換え、依存関係をインストールする必要があります。

    private static IWindsorContainer _container;
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        ConfigureWindsor(GlobalConfiguration.Configuration);
    }

    public static void ConfigureWindsor(HttpConfiguration configuration)
    {
        _container = new WindsorContainer();
        _container.Install(FromAssembly.This());
        _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
        var dependencyResolver = new WindsorDependencyResolver(_container);
        configuration.DependencyResolver = dependencyResolver;
    }    
71
Sumit Deshpande

webapiのウィンザープラミング に関するMark Seemannの投稿を読んでください。

6
Crixo

私はキャッスルウィンザーを直接操作しませんでしたが、ロジックは同様であるべきだと思います:

WatchControllerトラクターは次のようになります。

public WatchController(iWatch watch) 
{
    _watch = watch;
}

そして、ここが依存関係のinjectです。

WatchControllerクラスを登録するロケーターと同等のものが必要であり、必要なものに応じて受信するウォッチを指定します...デザイン/実行時間、曜日、乱数.. 。何でも機能し、必要なものは何でも...

次のコードはMVVM-Lightからのものですが、上記の段落を明確にする必要があります。

static ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    // This will run in design mode, so all your VS design data will come from here
    if (ViewModelBase.IsInDesignModeStatic)
    {
        SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
    }
    // This will run REAL stuff, in runtime
    else
    {
        SimpleIoc.Default.Register<IDataService, DataService>();
    }

    // You register your classes, so the framework can do the injection for you
    SimpleIoc.Default.Register<MainViewModel>();
    ...
}
1
Noctis