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の場合に依存性注入を実装する手順を教えてくれますか?前もって感謝します!
CodeCaster、Noctis、Cristianoは、すべてのヘルプとガイダンスに感謝します。上記のクエリに対する解決策を取得しました-
最初のステップは、nugetを使用してWindsor.Castleパッケージを WebApi ソリューションにインストールすることです。
次のコードスニペットを検討してください-
インターフェース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オブジェクトを通じて依存関係を注入しました。 IDependencyResolver と IDependencyScope を使用して、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;
}
webapiのウィンザープラミング に関するMark Seemannの投稿を読んでください。
私はキャッスルウィンザーを直接操作しませんでしたが、ロジックは同様であるべきだと思います:
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>();
...
}