2017-02-20 20 views
1

В настоящее время я пытаюсь обернуть класс в декоратор и ввести в одну из зависимостей во время выполнения. В настоящее время у меня есть интерфейс IStorage, который реализован StorageCacheDecorator и Storage. StorageCacheDecorator занимает IStorage и объект Storage object takes in a Context`. Однако объект контекста должен быть передан при каждом разрешении этих классов.Unity IoC InjectionFactory не соответствует DependencyOverride

public interface IStorage 
{ 

} 

public class Storage : IStorage 
{ 
    public Context Context { get; } 

    public Storage(Context context) 
    { 
     this.Context = context; 
    } 
} 

public class StorageCacheDecorator : IStorage 
{ 
    public IStorage InnerStorage { get; } 

    public StorageCacheDecorator(IStorage innerStorage) 
    { 
     this.InnerStorage = innerStorage; 
    } 
} 

public class Context 
{ 
} 

я опустил детали реализации и тест ниже приведен пример моей проблемы

[Test] 
    public void ShouldResolveWithCorrectContext() 
    { 
     var context = new Context(); 

     var container = new UnityContainer(); 

     container.RegisterType<Storage>(); 

     container.RegisterType<IStorage>(
      new InjectionFactory(c => new StorageCacheDecorator(
       c.Resolve<Storage>()))); 

     var resolve = container.Resolve<IStorage>(new DependencyOverride<Context>(context)); 

     Assert.That(resolve, Is.TypeOf<StorageCacheDecorator>()); 

     var cacheDecorator = ((StorageCacheDecorator)resolve); 
     Assert.That(cacheDecorator.InnerStorage, Is.TypeOf<Storage>()); 

     var storage = ((Storage)cacheDecorator.InnerStorage); 
     Assert.That(storage.Context, Is.SameAs(context)); 
    } 

Однако если мы удалим декоратора тест проходит

[Test] 
    public void ShouldResolveWithCorrectContext1() 
    { 
     var context = new Context(); 

     var container = new UnityContainer(); 

     container.RegisterType<IStorage, Storage>(); 

     var resolve = container.Resolve<IStorage>(new DependencyOverride<Context>(context)); 

     Assert.That(resolve, Is.TypeOf<Storage>()); 

     Assert.That(((Storage)resolve).Context, Is.SameAs(context)); 
    } 

Как сделать Я получаю InjectionFactory, чтобы уважать DependencyOverride?

+0

Я борюсь с той же проблемой. На самом деле вам даже не нужна сложная настройка. Если у вас нет декоратора, но зарегистрировано хранилище с делегатом фабрики, проблема будет такой же. Вы нашли решение случайно? – quetzalcoatl

+1

@quetzalcoatl Я не нашел решения, которое просто отказалось от этого контейнера IoC –

+0

Хех. Да, я бы хотел, чтобы я смог и бросил в DryIoc или что-то еще, даже простой «Виндзор» более предсказуем. Однако слишком много кода для изменения в этом старом проекте и слишком мало времени для этого. Спасибо за быстрый ответ! Возможно, я найду что-нибудь и опубликую позже. – quetzalcoatl

ответ

1

Прежде всего, я уверен, что то, что вы (и сегодня - я) наткнулись на ошибку в 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, так как ему нужно получить все параметры, указанные спереди.

Тем более причина для ненависти Единство ..

 Смежные вопросы

  • Нет связанных вопросов^_^