c# - Castle Windsor: implementación múltiple de una interfaz
dependency-injection castle-windsor (4)
Al registrar componentes en Castle Windsor, ¿cómo vinculamos la implementación específica de una interfaz a un componente que tiene una dependencia en esa interfaz? Sé de antemano qué implementación debe ser utilizada por el componente.
Por ejemplo, creé una aplicación de consola de muestra basada en el código de varios blogs y tutoriales.
A continuación está el código.
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();
}
}
Me gustaría que NortonService siempre use un Filelogger y McAfeeService para usar un registrador de base de datos.
En el programa anterior, no puedo vincular NortonService a FileLogger.
¿Cómo hacerlo?
Mi respuesta tal vez no sea la mejor, puede usar el método de nomenclatura para resolver la implementación múltiple:
container.Register(Component.For(typeof(ILogger))
.ImplementedBy(typeof(FileLogger))
.Named("FileLoggerIoC")
.LifestylePerWebRequest() ,
Component.For(typeof(ILogger))
.ImplementedBy(typeof(DatabaseLogger))
.Named("DatabaseLoggerIoC")
.LifestylePerWebRequest());
En sus funciones de llamada, debe resolverlo por su nombre: -
var fileLog = container.Resolve("FileLoggerIoC", typeof(ILogger));
var DbLog = container.Resolve("DatabaseLoggerIoC", typeof(ILogger));
El método de la mina quizás no sea el mejor, ya que a las personas no les gusta el localizador de servicios para obtener los componentes, puede usar esto como una solución temporal.
Tuve un problema como este, dos implementaciones de una interfaz y dos implementaciones de otra. Quería forzar el uso de implementaciones particulares de esas interfaces.
Mi estructura de clase se veía así:
Miré la convención de nombres, pero realmente no me gustó. En cambio, utilicé lo siguiente:
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>())
);
La información completa sobre este enfoque está aquí . En el código fuente proporcionado con esa publicación, muestro otras dos maneras de lograr el mismo resultado.
Si desea hacerlo en tiempo de ejecución, esto se puede lograr a través de IHandlerSelector. Escriba una clase que implemente IHandlerSelector. Proporciona un método SelectHandler que le permitirá definir la condición para el enlace condicional en el tiempo de ejecución. Un controlador en este caso es un componente en Windsor que participa en la construcción de instancias. Consulte aquí para más detalles.
Las respuestas anteriores me llevan a las dependencias en línea y el servicio de función reemplaza
Aquí está el código de registro:
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();
Salida:
McAfee Scan has Logged data to a database
Norton Scan has Logged data to a file