Прежде всего, я уверен, что то, что вы (и сегодня - я) наткнулись на ошибку в Unity.
Мне удалось немного диагностировать его благодаря this excellent article, откуда я привел пример BuilderStrategy
. После того, как я заменил InjectionFactory этим расширением, он работал в некоторых случаях и не работал для других.
После некоторого исследования, кажется, что все решения в единстве работать против того же Container
и его конфигурацию, и любой DependencyOverrides
валяются вызов в виде IBuilderContext
объекта, в наборе resolverOverrides
, который содержит policies
построенных из DependencyOverrides
. IBuilderContext
предоставляет метод GetResolverOverride(Type)
, который позволяет реализации получать значения переопределения для данного типа.
Итак, если вы явно запросите IBuilderContext.GetResolverOverride
для переопределения для вашего хранилища Context
, вы получите тот же объект-объект, который вы ожидаете.
Однако, если вы когда-нибудь попытаетесь спросить о контейнере, вы получите объект Контекста, разрешенный стандартными правилами. Не то, что переопределено в точке разрешения.
Поэтому любая попытка container.Resolve(..)
в InjectionFactory делегатом, как здесь:
container.RegisterType<IStorage>(
new InjectionFactory(c => new StorageCacheDecorator(
c.Resolve<Storage>()))); // <-- this C is Container
не сможет удовлетворить переопределение, потому что .. Контейнер не имеет ни малейшего представления о переопределениях!
Это должно было бы быть:
container.RegisterType<IStorage>(
new InjectionFactory(c => new StorageCacheDecorator(
builderContext.Resolve<Storage>())));
, который, если когда-либо реализована, может получить доступ к GetResolverOverride
и построить правильное хранение с правильным переопределением. Однако такой метод не существует, и, что еще хуже, на данный момент кода у вас нет доступа к IBuilderContext
. InjectionFactory не дает его вам. Возможно, к ним могут подключаться только расширения &.
Забавный факт: IBuilderContext
имеет GetResolverOverride
, но не имеет никаких видов .Resolve
.Итак, если вы возьмете код из этой статьи, и если вы использовали PreBuildUp
, как в этой статье, чтобы сделать свою собственную логику разрешения, вам придется либо использовать Container (и не выполнять переопределения), либо попасть в ад проверки всего и выполнения вручную всех подрешений, необходимых для создания экземпляра. IBuilderContext
предлагает вам довольно красивый метод NewBuildUp()
, который кажется отличным ярлыком, но .. он использует базовый контейнер и не перенаправляет переопределения.
Видя, насколько сложным и неинтуитивным оно является, и насколько легко случайно отказаться от этого набора resolveroverrides и отступить к ванильному контексту, я уверен, что InjectionFactory - это EVIL/BUGGED/MISDESIGNED/etc: он дает вам " c ", ваш основной экземпляр контейнера, так что у вас просто нет способа правильно разрешить параметры относительно переопределений. Вместо этого основного контейнера мы должны получить какой-то производный контейнер или дополнительный объект-строитель и т. Д.
Используя код, найденный в статье, я смог взломать что-то, что использовало этот GetResolverOverride
для инициализации нового объекта экземпляр каким-то образом я хотел, но он никоим образом не был универсальным и полностью не использовался повторно (я просто назвал правильный .GetResolverOverride, чтобы получить значение и передать его прямо в new MyObject(value)
.. но, боже, это просто ужасно. часть тестовой установки, поэтому я смогу жить с ней до тех пор, пока код не будет реорганизован.
Теперь вернемся к вашему делу. Очевидно, вы могли бы сделать подобное, но, оказывается, в случае декораторов th Это намного проще: просто избавьтесь от InjectionFactory. Ваш исходный код инициализации экземпляра, вероятно, более сложным, но если по какой-либо шанс, что это на самом деле было так просто, как в вашем примере:
container.RegisterType<IStorage>(
new InjectionFactory(c =>
new StorageCacheDecorator( // <- NEW &
c.Resolve<Storage>() // <- RESOLVE
)));
вы должны на самом деле используется декларативно вместо повелительного багги InjectionFactory:
container.RegisterType<IStorage, StorageCacheDecorator>(
new InjectionConstructor(
new ResolvedParameter<Storage>()
));
чистый эффект точно такой же: создается новый объект, вызывается конструктор с одним аргументом, а Storage
разрешен и используется в качестве этого аргумента. Я попробовал это с вашими тестовыми примерами, и все прошло отлично.
Вместо «ResolvedParameter` вы также можете использовать экземпляры прямых объектов, как:
container.RegisterType<IStorage, StorageCacheDecorator>(
new InjectionConstructor(
"foo", 5, new Shoe()
));
, но, так же, как с new StorageDecorator
в оригинальном примере InjectionConstructor должен получить все параметров для конструктор, и, очевидно, мы не получим ошибку времени компиляции при изменении параметров конструктора в будущем. Это также намного более ограничено, чем InjectionFactory, так как ему нужно получить все параметры, указанные спереди.
Тем более причина для ненависти Единство ..
Я борюсь с той же проблемой. На самом деле вам даже не нужна сложная настройка. Если у вас нет декоратора, но зарегистрировано хранилище с делегатом фабрики, проблема будет такой же. Вы нашли решение случайно? – quetzalcoatl
@quetzalcoatl Я не нашел решения, которое просто отказалось от этого контейнера IoC –
Хех. Да, я бы хотел, чтобы я смог и бросил в DryIoc или что-то еще, даже простой «Виндзор» более предсказуем. Однако слишком много кода для изменения в этом старом проекте и слишком мало времени для этого. Спасибо за быстрый ответ! Возможно, я найду что-нибудь и опубликую позже. – quetzalcoatl