2017-02-20 1 views
1

현재 클래스를 데코레이터에 래핑하고 런타임에 의존성 중 하나에 주입하려고합니다. 현재 StorageCacheDecoratorStorage에 의해 구현되는 IStorage의 인터페이스가 있습니다. StorageCacheDecoratorIStorageStorage 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 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)); 
    } 

를 전달하는 장식을 제거하면

[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)); 
    } 

그러나 내 문제의 예를 제공합니다 InjectionFactoryDependencyOverride을 존중합니까?

+0

나는 같은 문제로 싸우고 있습니다. 실제로 복잡한 설정이 필요하지 않습니다. 데코레이터가없고 Factory Delegate가있는 Storage를 등록한 경우 문제는 동일합니다. 우연히 어떤 해결책을 찾았습니까? – quetzalcoatl

+1

@quetzalcoatl 나는이 IoC 컨테이너를 그냥 포기한 해결책이 없음을 알아 냈습니다. –

+0

Heh. 그래, 내가 똑같이 할 수 있었으면 좋겠어. DryIoc이나 무엇이든, 심지어 평범한 '윈저'가 더 예측 가능하길. 그러나이 오래된 프로젝트에서 변경하기에는 너무 많은 코드가 필요하고 너무 적은 시간이 필요합니다. 빠른 답변을 보내 주셔서 감사합니다! 아마 뭔가를 찾아 나중에 게시 할 것입니다. – quetzalcoatl

답변

1

우선, 당신 (그리고 오늘)이 우연히 만난 것은 Unity의 버그입니다.

나는 BuilderStrategy의 예가 나온 this excellent article 덕분에 약간의 문제를 진단 할 수있었습니다. InjectionFactory를이 Extension으로 바꾼 후에는 어떤 경우에는 효과가 있었고 다른 경우에는 효과가 없었습니다. 일부 조사 후

, 유니티의 모든 해상도 DependencyOverrides로 구성 policies 포함 resolverOverrides 세트에서, IBuilderContext 객체의 호출을 따라 드래그하는 것과 같은 Container과 그 구성 및 DependencyOverrides에 반대 것으로 보인다. IBuilderContextGetResolverOverride(Type) 메서드를 제공합니다.이 메서드를 사용하면 특정 Type에 대한 값 재정의를 가져올 수 있습니다.

따라서 IBuilderContext.GetResolverOverride에 저장소 Context에 대한 재정의를 명시 적으로 요청하면 예상 한 것과 동일한 컨텍스트 개체가 표시됩니다.

그러나 컨테이너 자체에 질문하려고하면 표준 규칙에 따라 해결 된 Context 객체가 표시됩니다. 그게 해결 시점에서 무시되지는 않습니다.

이것은 왜 여기처럼 InjectionFactory 위임에 container.Resolve(..)을 시도 .. 컨테이너 재정에 대해 아무 생각이 없기 때문에

container.RegisterType<IStorage>(
     new InjectionFactory(c => new StorageCacheDecorator(
      c.Resolve<Storage>()))); // <-- this C is Container 

는, 오버라이드 (override)를 충족 할 수 없게됩니다!

이 될 것이다 :

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

이있는, 지금까지 구현 된 경우 GetResolverOverride에 액세스하고 올바른 오버라이드 적절한 스토리지를 구축 할 수 있습니다. 그러나 그런 방법은 존재하지 않으며, 더 나쁜 것은 코드의이 시점에서 IBuilderContext에 액세스 할 수 없습니다. InjectionFactory가 제공하지 않습니다. 아마 유일한 확장 & 친구가 액세스 할 수 있습니다.

재미있는 사실 : IBuilderContextGetResolverOverride이지만 어떤 종류의 .Resolve도 없습니다.따라서 해당 기사에서 코드를 가져오고 해당 기사의대로 PreBuildUp을 사용하여 독자적인 해결 논리를 작성했다면 컨테이너를 사용하고 (해결사 무시시 실패) 또는 지옥에 들어가야합니다 모든 것을 검사하고 인스턴스를 구성하는 데 필요한 모든 하위 해상도를 수동으로 수행해야합니다. IBuilderContext은 멋진 모양 인 NewBuildUp() 메서드를 제공하지만 기본 컨테이너를 사용하며 해결 프로그램 재정의를 전달하지 않습니다.

얼마나 복잡하고 직관적이지 않은지, 실수로 바뀌는 버그 수정과 폴백을 바닐라 컨텍스트에 떨어 뜨리는 것이 얼마나 쉬운지를 보면, 나는 InjectionFactory가 악의적/사고/잘못 판단 된/etc라고 확신합니다. c "를 사용하여 오버라이드와 관련하여 매개 변수를 제대로 해석 할 방법이 없습니다. 그 주 컨테이너 대신, 우리는 일종의 파생 컨테이너 나 여분의 빌더 객체 등을 가져야합니다.

문서에있는 코드를 사용하여이 객체를 해킹 할 수있었습니다. GetResolverOverride을 사용하여 새 객체를 초기화합니다. 나는 원했던 방식으로 인스턴스를 만들었지 만 재사용 할 수있는 일반적인 방법이 아니 었습니다. (그냥 적절한 .GetResolverOverride를 호출하여 값을 얻고 바로 new MyObject(value)에 전달했습니다.하지만 신이 끔찍합니다. 테스트 설정의 일부이므로 코드가 리팩토링 될 때까지 그 코드와 함께 살 수 있습니다.

자, 이제 다시 돌아가 보겠습니다. 비슷한 일을 할 수는 있지만, 장식 자의 경우 번째 ere는 훨씬 쉬운 방법입니다. 단지 InjectionFactory를 제거하십시오. 원래 인스턴스 초기화 코드는 아마 더 복잡하지만, 혹시 경우 실제로 예에서와 같이 간단했다 : 당신이 실제로 대신 필수적 버그 InjectionFactory의 선언적인 방법을 사용해야

container.RegisterType<IStorage>(
     new InjectionFactory(c => 
      new StorageCacheDecorator( // <- NEW & 
       c.Resolve<Storage>() // <- RESOLVE 
     ))); 

:

container.RegisterType<IStorage, StorageCacheDecorator>(
      new InjectionConstructor(
       new ResolvedParameter<Storage>() 
     )); 

그물 효과는 완전히 동일합니다. 새 개체가 만들어지고 단일 인수 생성자가 호출되고 Storage이 확인 된 후 해당 인수로 사용됩니다. 나는 당신의 테스트 케이스와 함께 그것을 시험해 보았고 그것은 잘 작동했다. 대신 당신은 또한 같은 직접 객체 인스턴스를 사용할 수 있습니다 ResolvedParameter` '의

: 당신의 원래의 예에서 new StorageDecorator와 마찬가지로,

container.RegisterType<IStorage, StorageCacheDecorator>(
      new InjectionConstructor(
       "foo", 5, new Shoe() 
     )); 

을하지만은 InjectionConstructor은 대한 매개 변수의 모든를 얻기 위해 필요 나중에 생성자 매개 변수가 변경되면 컴파일 타임 오류가 발생하지 않습니다. InjectionFactory보다 훨씬 제한적입니다. 모든 매개 변수를 앞에 명시해야하기 때문입니다.

Unity를 증오하는 데 더 많은 이유가 있습니다.

관련 문제