Как использовать разные реализации в разных местах с контейнером IoC
Мне интересно, как использовать разные реализации одного интерфейса в разных клиентах. Вот пример ситуации.
public interface IRandomIntGenerator
{
int Generate();
}
public class SimpleRandomIntGenerator : IRandomIntGenerator
{
public int Generate()
{
return new Random().Next();
}
}
public class CryptoServiceProviderRandomIntGenerator : IRandomIntGenerator
{
public int Generate()
{
var generator = new RNGCryptoServiceProvider();
byte[] bytes = new byte[4];
generator.GetBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
}
Тогда у меня есть два клиента, которые не хотят знать о конкретных реализациях. Один генерирует коды входа в систему, а другой выбирает случайный элемент массива.
public class LogInCodeGenerator
{
private readonly IRandomIntGenerator randomIntGenerator;
public LogInCodeGenerator(IRandomIntGenerator randomIntGenerator)
{
this.randomIntGenerator = randomIntGenerator;
}
public string GenerateCode(int length)
{
var builder = new StringBuilder(length);
for (int i = 0; i < length; i++)
builder.Append(randomIntGenerator.Generate() % 10);
return builder.ToString();
}
}
public class RandomArrayItemChoose
{
private readonly IRandomIntGenerator randomIntGenerator;
public RandomArrayItemChoose(IRandomIntGenerator randomIntGenerator)
{
this.randomIntGenerator = randomIntGenerator;
}
public string Choose(string[] arr)
{
return arr[randomIntGenerator.Generate() % arr.Length];
}
}
Я хочу настроить контейнер IoC таким образом, чтобы он использовал SimpleRandomIntGenerator
за RandomArrayItemChoose
а также CryptoServiceProviderRandomIntGenerator
за LogInCodeGenerator
,
Есть ли способ сделать это с любым популярным контейнером IoC для.NET? Особенно меня интересует Виндзорский замок.
4 ответа
Тогда у меня есть два клиента, которые не хотят знать о конкретных реализациях.
Кажется, что в вашем дизайне есть некоторая неопределенность. Эти клиенты могут не интересоваться точной реализацией, но в вашей системе, похоже, есть какое-то требование, чтобы LogInCodeGenerator
должен работать с криптографически случайными числами.
Поскольку это требование для обеспечения безопасности системы, было бы лучше сделать проект явно об этом. Другими словами, здесь вы говорите о двух отдельных контрактах:
interface IRandomIntGenerator { }
interface ICryptographicRandomIntGenerator { }
Это не только делает цель кода более понятной, но устранение этой неоднозначности из проекта значительно упрощает настройку DI.
Используйте переопределения сервиса Windsor. См. Раздел "Поставка компонента для использования в зависимости" в Windsor Docs.
Просто явно объявите зависимости:
Component.
For<IRandomIntGenerator>().
ImplementedBy<SimpleRandomIntGenerator>().
Named("SimpleRandomIntGenerator"),
Component.
For<IRandomIntGenerator>().
ImplementedBy<CryptoServiceProviderRandomIntGenerator>().
Named("CryptoServiceProviderRandomIntGenerator"),
Component.
For<RandomArrayItemChoose>().
DependsOn(Dependency.
OnComponent("randomIntGenerator", "SimpleRandomIntGenerator")),
Component.
For<LogInCodeGenerator>().
DependsOn(Dependency.
OnComponent("randomIntGenerator", "CryptoServiceProviderRandomIntGenerator")),
Вы можете назвать свои зарегистрированные экземпляры, а затем получить к ним доступ по их имени. В вашем случае, если я понял ваш вопрос, вы хотите другую инъекцию, основанную на классе, который запрашивает разрешенный экземпляр. Таким образом, вы можете назвать ваши зарегистрированные экземпляры по имени типа вашего запрашивающего класса.
Тебе придется resolve
ваши экземпляры вручную:
IRandomIntGenerator generator = container.Resolve<IRandomIntGenerator>(GetType().Name);