На основании сообщения об ошибке вы пытаетесь ввести Task<IReportedIssueManager>
вместо своей фабрики. Это может быть все, что вам нужно знать.
Вам также не нужно иметь «базовый интерфейс», а затем другой интерфейс для каждой реализации. Смысл одного интерфейса в том, что он может иметь несколько реализаций. Если у нас есть несколько реализаций одного интерфейса, то объявление наследуемых интерфейсов, по одной на реализацию, является излишним.
Другими словами, все, что возвращает ваш завод, будет реализацией IReportedIssueManager
, приведенной к IReportedIssueManager
. Нисходящий код не будет знать, реализует ли он также ICreateReportedXIssueManager
, ICreateReportedYIssueManager
или ICreateReportedZIssueManager
. Там может быть никаких оснований для существования этих интерфейсов. Даже если есть причина, это не имеет значения, если вызывающий ожидает и получает IReportedIssueManager
.
Виндзор предоставляет способ создания фабрики. Это не намного более кратко, но это приводит к тому, что каждый контейнер создается каждый раз, когда он создается.
Во-первых, независимо от того, как вы реализуете фабрику, нет причин для ее асинхронности. Это просто создание объекта. Async предназначен для более длительных процессов ввода-вывода, где поток может быть освобожден, чтобы сделать что-то еще, пока не получен какой-либо ответ. Вот синхронный интерфейс:
public interface IReportedIssueFactory
{
// I used the enum here, but you could use the int instead.
IReportedIssueManager CreateIssueManager(IssueType issueType);
}
В вашей регистрации зависимостей добавьте TypedFactoryFacility
в свой контейнер:
container.AddFacility<TypedFactoryFacility>();
Затем зарегистрируйте все реализации IReportedIssueManager
, дав им имена (что будет важно позже). Поскольку дело в том, что все они реализуют IReportedIssueManager
, для целей этой фабрики не имеет значения, если они классы реализуют другие интерфейсы. Зарегистрируйте их в соответствии с их конкретными типами.
В этом примере я использую имена типов для именования зависимостей только потому, что он избегает добавления константы "волшебная строка". Фактические имена могут быть любыми по вашему выбору, но они не важны, за исключением того, что вы используете их в другом месте.
container.Register(
Component.For<IReportedIssueManager, CreateReportedXIssueManager>()
.Named(typeof(CreateReportedXIssueManager).FullName),
Component.For<IReportedIssueManager, CreateReportedYIssueManager>()
.Named(typeof(CreateReportedYIssueManager).FullName),
Component.For<IReportedIssueManager, CreateReportedZIssueManager>()
.Named(typeof(CreateReportedZIssueManager).FullName)
);
Затем создайте класс, который будет принимать входные данные (в данном случае issueTypeId
) и возвращает имя правильной зависимости:
public class IssueManagerSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
var issueType = (IssueType)arguments[0];
switch (issueType)
{
case IssueType.Listing:
{
return typeof(CreateReportedXIssueManager).FullName;
}
case IssueType.Post:
{
return typeof(CreateReportedYIssueManager).FullName;
}
case IssueType.User:
{
return typeof(CreateReportedZIssueManager).FullName;
}
default:
// So I didn't have to create an exception type as I tested.
throw new Exception("Unknown type");
}
}
}
Наконец, зарегистрируйте в своем контейнере следующее:
container.Register(Component.For<IReportedIssueFactory>()
.AsFactory(new IssueManagerSelector()));
Вам не нужно создавать реализацию IReportedIssueFactory
. Вы можете просто внедрить интерфейс, и Windsor предоставит реализацию.
Будет выполнено следующее:
- Когда вы вызываете CreateIssueManager
, он передаст ваш IssueType
аргумент методу GetComponentName
, равному IssueManagerSelector
.
- Этот метод выберет имя компонента и вернет его.
- Затем фабрика разрешит компонент с этим именем и вернет его.
Фабрика работает по договоренности. Виндзор предполагает, что метод интерфейса фабрики, который возвращает что-то, является методом «создания». Вот почему нам не нужно было давать ему конкретное имя.
Вот модульный тест, чтобы убедиться, что он работает как положено:
[TestMethod]
public void WindsorFactoryTest()
{
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<IReportedIssueManager, CreateReportedXIssueManager>()
.Named(typeof(CreateReportedXIssueManager).FullName),
Component.For<IReportedIssueManager, CreateReportedYIssueManager>()
.Named(typeof(CreateReportedYIssueManager).FullName),
Component.For<IReportedIssueManager, CreateReportedZIssueManager>()
.Named(typeof(CreateReportedZIssueManager).FullName)
);
container.Register(Component.For<IReportedIssueFactory>()
.AsFactory(new IssueManagerSelector()));
var factory = container.Resolve<IReportedIssueFactory>();
Assert.IsInstanceOfType(factory.CreateIssueManager(IssueType.Listing), typeof(CreateReportedXIssueManager));
Assert.IsInstanceOfType(factory.CreateIssueManager(IssueType.Post), typeof(CreateReportedYIssueManager));
Assert.IsInstanceOfType(factory.CreateIssueManager(IssueType.User), typeof(CreateReportedZIssueManager));
}
Наконец, вот Виндзорская документация .