У меня возникла проблема, и я не могу понять, как ее решить. У меня в основном есть заводской шаблон, настроенный как таковой:
Это мой базовый интерфейс
public interface IReportedIssueManager
{
Task<BaseManagerResponse> ManageRequest(ReportedIssueRequest request);
}
Кроме того, у меня есть фабричный класс, который определяет, какую службу следует вернуть на основе определенного типа (createReportedXIssueManager, createReportedYIssueManager или createReportedZIssueManager). В моем контроллере я бы создал экземпляр этого класса, вызвал бы этот метод и передал бы тип:
public class ReportedIssueFactory : IReportedIssueFactory
{
private readonly ICreateReportedXIssueManager createReportedXIssueManager;
private readonly ICreateReportedYIssueManager createReportedYIssueManager;
private readonly ICreateReportedZIssueManager createReportedZIssueManager;
public ReportedIssueFactory(
ICreateReportedXIssueManager createReportedXIssueManager,
ICreateReportedYIssueManager createReportedYIssueManager,
ICreateReportedZIssueManager createReportedZIssueManager)
{
this.createReportedXIssueManager = createReportedXIssueManager;
this.createReportedYIssueManager= createReportedYIssueManager;
this.createReportedZIssueManager= createReportedZIssueManager;
}
public async Task<IReportedIssueManager> ReportIssue(int issueTypeId)
{
var issueType = (IssueType)issueTypeId;
switch(issueType)
{
case IssueType.Listing:
return createReportedXIssueManager;
case IssueType.Post:
return createReportedYIssueManager;
case IssueType.User:
return createReportedZIssueManager;
default:
throw new ValidationException(ReportedIssuesServiceResources.UnknownIssueType);
}
}
}
и каждая из этих служб настроена как таковая:
public interface ICreateReportedXIssueManager : IReportedIssueManager
{
Task<BaseManagerResponse> ManageRequest(CreateReportedXIssueRequest request);
}
and
public interface ICreateReportedYIssueManager : IReportedIssueManager
{
Task<BaseManagerResponse> ManageRequest(CreateReportedYIssueRequest request);
}
and
public interface ICreateReportedZIssueManager : IReportedIssueManager
{
Task<BaseManagerResponse> ManageRequest(CreateReportedZIssueRequest request);
}
и, наконец, в моем контроллере я бы назвал что-то вроде этого:
var manager = await _reportedIssueFactory.ReportIssue(IssueTypeId);
var response = await manager.ManageRequest(request);
Моя проблема в том, что, поскольку это фабричный шаблон, я думаю, что Windsor ожидает службу с тем же именем, что и базовый интерфейс?
Я получаю следующую ошибку:
Нет компонента для поддержки службы System.Threading.Tasks.Task`1[[.....ReportedIssues.IReportedIssueManager]]
Кто-нибудь знает, как правильно зарегистрироваться в Виндзоре в этом случае? Любая помощь очень ценится, спасибо!
Судя по сообщению об ошибке, вы пытаетесь внедрить Task<IReportedIssueManager>
вместо фабрики. Это может быть все, что вам нужно знать.
Вам также не нужно иметь «базовый интерфейс», а затем еще один интерфейс для каждой реализации. Смысл одного интерфейса в том, что он может иметь несколько реализаций. Если у нас есть несколько реализаций одного интерфейса, то объявление унаследованных интерфейсов, по одному на реализацию, излишне.
Другими словами, все, что возвращает ваша фабрика, будет реализацией IReportedIssueManager
, преобразованной в IReportedIssueManager
. Нисходящий код не будет знать, реализует ли он также ICreateReportedXIssueManager
, ICreateReportedYIssueManager
или ICreateReportedZIssueManager
. Может вообще не быть причин для существования этих интерфейсов. Даже если есть причина, здесь не имеет значения, ожидает ли звонящий и получает IReportedIssueManager
.
Виндзор предоставляет возможность создать фабрику. Это не намного более лаконично, но это приводит к разрешению всего из контейнера каждый раз, когда он создается.
Во-первых, независимо от того, как вы реализуете фабрику, нет причин для ее асинхронности. Это просто создание объекта. Асинхронный режим предназначен для более длительных процессов ввода-вывода, когда поток может быть освобожден для выполнения каких-либо других действий, пока ожидается какой-либо ответ. Вот синхронный интерфейс:
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));
}
Наконец, вот документация Виндзора.
о боже мой, спасибо вам большое! Это было очень полезно! Спасибо! :)