Reputation: 691
While registering components in Castle Windsor, how do we bind specific implementation of an interface to a component that has a dependency on that interface. I know in advance which implementation needs to be used by the component.
For example i created a sample console application based on code from several blogs and tutorials.
Following is the code.
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();
}
}
I would like NortonService to always use a Filelogger and McAfeeService to use a Database Logger.
In the above program i am unable to bind NortonService to FileLogger.
How to do it?
Upvotes: 27
Views: 21625
Reputation: 691
The above answers lead me to inline dependencies and the feature service override
Here is the registration code:
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();
Output:
McAfee Scan has Logged data to a database
Norton Scan has Logged data to a file
Upvotes: 31
Reputation: 1926
If you want to do it at runtime, This can be acheived through IHandlerSelector. Write a class that implements IHandlerSelector. It provides a method SelectHandler which will let you define the condition for binding conditionally at runtime. A Handler in this case is a component in Windsor that participates in instance construction. Refer here for more details.
Upvotes: 3
Reputation: 5471
I had a problem very like this, two implementation of one interface and two implementation of another interface. I wanted to force usage of particular implementations of those interfaces.
My class structure looked like this -
I looked at the naming convention, but didn't really like it. Instead I used the following -
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>())
);
Full info about this approach is here. In the source code provided with that post I show two other ways of achieving the same result.
Upvotes: 11
Reputation: 291
My answer maybe not the best one, you can use naming method to resolve multi implementation:
container.Register(Component.For(typeof(ILogger))
.ImplementedBy(typeof(FileLogger))
.Named("FileLoggerIoC")
.LifestylePerWebRequest() ,
Component.For(typeof(ILogger))
.ImplementedBy(typeof(DatabaseLogger))
.Named("DatabaseLoggerIoC")
.LifestylePerWebRequest());
In your calling functions, you need to resolve it by name :-
var fileLog = container.Resolve("FileLoggerIoC", typeof(ILogger));
var DbLog = container.Resolve("DatabaseLoggerIoC", typeof(ILogger));
Mine method maybe not the best one as people don't like service locator to get the components, you can use this as temporary solution.
Upvotes: 3