Я покажу большую часть своей сантехники, но я постараюсь свести ее к минимуму, чтобы вопрос оставался простым.
Одна из моих конечных точек API полагается на внешних поставщиков для завершения вызова. Когда пользователь отправляет запрос к этой конечной точке, он может указать, какого поставщика они хотят, чтобы мы использовали при обработке запроса, скажем, поставщиками являются Bing и Google.
Итак, у меня есть IProvider
интерфейс и две конкретные реализации BingProvider
и GoogleProvider
(в моем реальном API интерфейс провайдера на самом деле является универсальным интерфейсом, но я оставляю универсальные шаблоны, чтобы не сделать это более беспорядочным, чем должно быть). Мне нужно определить правильного поставщика на основе поля в запросе. Simple Injector не позволяет регистрировать несколько конкретных реализаций одного и того же интерфейса, поэтому мне приходится использовать фабрику; Я создаю такой, который выглядит примерно так:
public class ProviderFactory
{
private readonly Func<string, IProvider> _Selector;
public ProviderFactory(Func<string, IProvider> selector)
{
this._Selector = selector;
}
public IProvider Get(string provider)
{
return this._Selector(provider);
}
}
Я регистрирую свою фабрику в контейнере, выполнив примерно следующее:
container.RegisterSingleton<ProviderFactory>(new ProviderFactory(provider =>
{
switch (provider)
{
case "Bing":
return container.GetInstance<BingProvider>()
case "Google":
return container.GetInstance<GoogleProvider>()
default:
throw new ArgumentOutOfRangeException("Unknown provider: " + provider);
}
}));
Я это тестирую. Оно работает. Фантастика.
Теперь мне нужно создать и зарегистрировать несколько декораторов для моего IProvider
. В каждой конкретной реализации IProvider
эти декораторы должны применяться, когда контейнер разрешает их. Ради этого примера допустим, что у меня есть Decorator1
и Decorator2
, которые реализуют IProvider
. Регистрирую их в контейнере вот так:
container.RegisterDecorator(typeof(IProvider), typeof(Decorator1), Lifestyle.Singleton);
container.RegisterDecorator(typeof(IProvider), typeof(Decorator2), Lifestyle.Singleton);
Вот где проблема. Когда моя фабрика разрешает экземпляр BingProvider
или GoogleProvider
, это именно то, что я получаю. Я хочу получить экземпляр Decorator2
, который украшает экземпляр Decorator1
, который, в свою очередь, украшает те конкретные реализации IProvider
, которые я запрашивал. Я предполагаю, что это связано с тем, что я специально не прошу контейнер разрешить экземпляр IProvider
, а скорее прошу его разрешить конкретную реализацию IProvider
.
Кажется, я запутался здесь, и я не уверен, как лучше всего решить эту проблему.
Изменить
Хорошо, подумав еще немного, я понимаю, почему декораторы никогда не будут разрешены. Возьмите, например, Decorator1
и BingProvider
. И Decorator1
, и BingProvider
реализуют IProvider
, но Decorator1
не реализуют BingProvider
, поэтому, когда я прошу Simple Injector разрешить экземпляр BingProvider
, он даже не может дать мне экземпляр Decorator1
. Я собираюсь попросить его каким-то образом разрешить экземпляр IProvider
, но дать мне правильную конкретную реализацию с установленными декораторами.
Отлично, что я понимаю, но теперь я менее уверен в том, что делать дальше.
Обновить
Основываясь на ответе Стивена, я изменил свою заводскую регистрацию следующим образом:
var bingProvider = Lifestyle.Singleton
.CreateProducer<IProvider, BingProvider>(container);
var googleProvider = Lifestyle.Singleton
.CreateProducer<IProvider, GoogleProvider>(container);
container.RegisterSingleton<ProviderFactory>(new ProviderFactory(provider =>
{
switch (provider)
{
case "Bing":
return bingProvider.GetInstance();
case "Google":
return googleProvider.GetInstance();
default:
throw new ArgumentOutOfRangeException("Unknown provider: " + provider);
}
}));
Моя новая проблема заключается в том, что когда я запускаю свой модульный тест для проверки контейнера, тест завершается с ошибкой, которая выглядит примерно так (мне пришлось исправить это сообщение об ошибке, чтобы оно соответствовало моему примеру здесь, надеюсь, это не приведет к тому, что что-то будет утрачено при переводе):
Конфигурация недействительна. Были получены следующие диагностические предупреждения:
- [Torn Lifestyle] Регистрация для IProvider соответствует той же реализации и образу жизни, что и регистрация для IProvider. Оба они сопоставляются с Decorator1 (Singleton). Это приведет к тому, что каждая регистрация будет разрешаться в другой экземпляр: каждая регистрация будет иметь свой собственный экземпляр.
- [Torn Lifestyle] Регистрация для IProvider соответствует той же реализации и образу жизни, что и регистрация для IProvider. Оба они сопоставляются с Decorator2 (Singleton). Это приведет к тому, что каждая регистрация будет разрешаться в другой экземпляр: каждая регистрация будет иметь свой собственный экземпляр.
См. свойство "Ошибка" для получения подробной информации о предупреждениях. См. https://simpleinjector.org/diagnostics, как исправить проблемы и как подавить отдельные предупреждения.