Castle Windsorにコンポーネントを登録するときに、インターフェイスの特定の実装を、そのインターフェイスに依存するコンポーネントにバインドする方法を教えてください。コンポーネントで使用する必要のある実装を事前に知っています。
たとえば、いくつかのブログやチュートリアルのコードに基づいてサンプルコンソールアプリケーションを作成しました。
コードは次のとおりです。
public interface IReport
{
void LogReport();
}
public interface ILogger
{
string Log();
}
public class FileLogger : ILogger
{
public string Log()
{
return "Logged data to a file";
}
}
public class DatabaseLogger : ILogger
{
public string Log()
{
return "Logged data to a database";
}
}
public class McAfeeService : IReport
{
private readonly ILogger _logger;
public McAfeeService(ILogger logger)
{
this._logger = logger;
}
public void LogReport()
{
string getLogResult = this._logger.Log();
Console.WriteLine("McAfee Scan has " + getLogResult);
}
}
public class NortonService : IReport
{
private readonly ILogger _logger;
public NortonService(ILogger logger)
{
this._logger = logger;
}
public void LogReport()
{
string getLogResult = this._logger.Log();
Console.WriteLine("Norton Scan has " + getLogResult);
}
}
class Program
{
private static IWindsorContainer container;
static void Main(string[] args)
{
// Register components
container = new WindsorContainer();
container.Register(Component.For<IReport>().ImplementedBy<NortonService>());
container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());
IReport service = container.Resolve<IReport>();
service.LogReport();
Console.ReadLine();
}
}
NortonServiceで常にFileloggerを使用し、McAfeeServiceでDatabase Loggerを使用したいと思います。
上記のプログラムでは、NortonServiceをFileLoggerにバインドできません。
どうやってするの?
上記の答えは、インラインの依存関係と機能 service override につながります
登録コードは次のとおりです。
container.Register(Component.For<IReport>().ImplementedBy<NortonService>().Named("nortonService"));
container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());
container.Register(Component.For<ILogger>().ImplementedBy<DatabaseLogger>());
container.Register(
Component.For<IReport>().ImplementedBy<McAfeeService>().Named("mcafeeService")
.DependsOn(Dependency.OnComponent<ILogger, DatabaseLogger>())
);
IReport mcafeescan = container.Resolve<IReport>("mcafeeService");
mcafeescan.LogReport();
IReport nortonscan = container.Resolve<IReport>("nortonService");
nortonscan.LogReport();
出力:
McAfee Scan has Logged data to a database
Norton Scan has Logged data to a file
私はこのような問題を抱えていました。1つのインターフェースの2つの実装と別のインターフェースの2つの実装です。それらのインターフェースの特定の実装を強制的に使用したかったのです。
私のクラス構造は次のようになりました-
私は命名規則を見ましたが、あまり好きではありませんでした。代わりに私は以下を使用しました-
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IMessageLoader>().ImplementedBy<MessageLoaderDatabase>()
,Component.For<IMessageLoader>().ImplementedBy<MessageLoaderFile>()
,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceDatabase>()
.DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderDatabase>())
,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceFile>()
.DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderFile>())
,Component.For<MessageOfTheDayController>().LifestyleTransient()
.DependsOn(Dependency.OnComponent<IMessageOfTheDayService, MessageOfTheDayServiceFile>())
);
このアプローチについての完全な情報は here です。その投稿で提供されているソースコードでは、同じ結果を得る他の2つの方法を示しています。
実行時に行う場合は、IHandlerSelectorを使用してこれを実現できます。 IHandlerSelectorを実装するクラスを記述します。これは、実行時に条件付きでバインドするための条件を定義できるメソッドSelectHandlerを提供します。この場合のハンドラーは、インスタンスの構築に関与するウィンザーのコンポーネントです。詳細は こちら を参照してください。
私の答えはおそらく最良のものではないでしょう、あなたはマルチ実装を解決するために命名方法を使うことができます:
container.Register(Component.For(typeof(ILogger))
.ImplementedBy(typeof(FileLogger))
.Named("FileLoggerIoC")
.LifestylePerWebRequest() ,
Component.For(typeof(ILogger))
.ImplementedBy(typeof(DatabaseLogger))
.Named("DatabaseLoggerIoC")
.LifestylePerWebRequest());
呼び出し関数では、名前で解決する必要があります:-
var fileLog = container.Resolve("FileLoggerIoC", typeof(ILogger));
var DbLog = container.Resolve("DatabaseLoggerIoC", typeof(ILogger));
Mineメソッドは、コンポーネントを取得するためのサービスロケーターが嫌いなため、おそらく最善の方法ではありません。これを一時的なソリューションとして使用できます。