Как зарегистрировать шаблон Service Factory с помощью базового интерфейса Castle Windsor

У меня возникла проблема, и я не могу понять, как ее решить. У меня в основном есть заводской шаблон, настроенный как таковой:

Это мой базовый интерфейс

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]]

Кто-нибудь знает, как правильно зарегистрироваться в Виндзоре в этом случае? Любая помощь очень ценится, спасибо!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
1 103
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Судя по сообщению об ошибке, вы пытаетесь внедрить 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));

}

Наконец, вот документация Виндзора.

о боже мой, спасибо вам большое! Это было очень полезно! Спасибо! :)

BellaGurl 30.05.2019 04:19

Другие вопросы по теме