2009-04-05 3 views
3

Ninject (또는 다른 컨테이너)를 사용하여 서비스를 요청하는 유형을 어떻게 알 수 있습니까?

public interface IFooService 
{ 
    void DoSomething(); 
} 

그리고 그 서비스의 일반적인 구현은 다음과 같습니다.

public class FooService<TRequestingClass> : IFooService 
{ 
    public virtual void DoSomething() { } 
} 

그리고 IFooService의 인스턴스를 필요로하는 다른 클래스가 있습니다.

public class Bar 
{ 
    private IFooService _fooService; 
    public Bar(IFooService fooService) 
    { 
     this._fooService = fooService; 
    } 
} 

Bar가 생성되면 FooService <Bar>의 생성자 인수가 전달되도록 IoC 컨테이너를 연결해야합니다. Bar와 같은 다른 많은 수업이 있습니다. 각각은 FooService <TRequestingClass>의 인스턴스를 필요로 할 수도 있습니다. TRequestingClass는 IFooService의 인스턴스를 필요로하는 클래스 유형입니다. IFooService의 소비자에게이 멍청함을 드러 낼 필요는 없습니다. 그들이주의해야 할 것은 전달 된 IFooService의 메서드를 호출 할 수 있다는 것입니다. 그들은 전달 된 IFooService의 구체적인 구현이 구축되는 데 특별한 것이 필요하다는 것을 알 필요가 없습니다.

FooService <T>에 대한 대안으로 생성자에 생성 될 클래스의 이름을 포함하는 문자열 인수를 가진 비 - 제네릭 클래스가됩니다. 예 :

public class FooService : IFooService 
{ 
    public FooService(string requestingClassName) { } 
} 

이 방법으로 종속성을 구성하려면 IoC 컨테이너를 연결하는 방법은 무엇입니까?

왜 그런 wierd 구조가 필요한지 혼란 스럽다면 log4net.LogManager.GetLogger (typeof (SomeClass))로 생성 된 ILog를 얻을 때 log4net이 가장 잘 작동하는 방법을 고려하십시오. 내가 log4net에 대한 참조를 내 코드를 쓰레기 싶지 않아, 그래서 간단한 ILogger 인터페이스를 작성하고 싶습니다 그리고 이런 식으로 구현 :

public class GenericLogger<T> : ILogger 
{ 
    private readonly ILog log; 

    public GenericLogger() 
    { 
     this.log = log4net.LogManager.GetLogger(typeof(T)); 
    } 

    public void Debug(object message) 
    { 
     this.log.Debug(message); 
    } 

    /* .... etc .... */ 
} 
+0

이 종속성 반전의 위반 : – Will

+0

종류. 그러나 ILogger의 인스턴스를 필요로하는 클래스는이 wierdness에 대해 신경 쓸 필요가 없습니다. 그것은 모두 디버그 등을 호출 할 수 있습니다. 내 ILogger의 구체적인 구현은 log4net을 사용합니다.이 클래스는 로깅을 수행하는 클래스의 이름을 알고 있으면 더 잘 작동합니다. 나는 이것이 괜찮다고 생각한다. – JohnRudolfLewis

답변

6

가장 쉬운 방법은 ILogger<T>를 생성하는 것입니다 인터페이스 :

public class ILogger<T> : ILogger { } 
public class GenericLogger<T> : ILogger<T> { ... } 

그런 다음 올바른 유형을 얻기 위해 제네릭 형식의 추론에 의존하고 있습니다. 그런 다음

Bind(typeof(ILogger<>)).To(typeof(GenericLogger<>)); 

당신이 소요되는 유형과 같을 것이다 :

public class FooService : IFooService { 
    public FooService(ILogger<FooService> logger) { ... } 
} 

당신이 ILogger<T> 인터페이스에 대해 강력히 경우, 당신은 예를 들어, Ninject에에서 다음 바인딩은 모든 당신이 필요로하는 것입니다 부모 유형을 결정하기 위해 IContext을 읽는 맞춤형 제공 업체처럼 더 독창적 인 작업을 수행 할 수 있습니다.

public class GenericLogger : ILogger { 
    public class GenericLogger(Type type) { ... } 
} 

public class LoggerProvider : Provider<ILogger> { 
    public override ILogger CreateInstance(IContext context) { 
    return new GenericLogger(context.Target.Member.ReflectedType); 
    } 
} 

그 다음이 소요되는 유형은 다음과 같이 작동합니다 :

public class FooService : IFooService { 
    public FooService(ILogger logger) { ... } 
} 
+0

나는 커스텀 공급자 아이디어를 좋아한다. 하지만 문법이 틀린 것 같아요 문맥은 어떨까요? 회원. 유형을 알 수 있습니까? 하지만 제 경우에는 LogInterceptor를 반환합니다. http://davidhayden.com/blog/dave/archive/2008/06/22/NinjectAOPCastlesDynamicProxy2SimpleInterceptorInterceptAttribute.aspx를 참조하십시오. – JohnRudolfLewis

+0

끝납니다. 나는 첫 번째 대답을 더 좋아합니다. 인터셉터를 일반적인 유형으로 만들어 내 다음 문제를 해결했습니다. 내가 한 일에 대한 블로그 게시물을 작성해야하는 것처럼 들립니다. – JohnRudolfLewis

1

내가 당신을 오해하고 있지 않다 경우, 단순히 GericLogger 생성자가없는 이유는 객체의 유형 인 매개 변수를 사용.그런 다음이 작업을 수행 :

ILog = kernel.Get<ILog>(ParameterList); 

나는 Ninject에 파라미터 목록에 완전히 아직 읽지 않은, 그러나 IParameterList를 사용하여 매개 변수 유형을 주입하는 방법이 될 것으로 보인다.

는 편집 :

은 다음과 같습니다

는 다음과 같이 작동합니다 : 다음

ILog = kernel.Get<ILog>(new ConstructorArgument[] { 
    new ConstructorArgument("ClassName", this.GetType().Name) 
}) 

당신이 당신의 ILOG는

class GenericLogger : Ilog 
{ 
    GenericLogger(string ClassName) {}; 
} 

내가 이것을 테스트하지 않았다가, 단지 무엇으로부터 것 같다 Ninject 소스 (최근 Ninject2 트리를보고 있습니다)

EDIT :

매개 변수가 아닌 ConstructorArgument를 전달하려고합니다. 반영하도록 업데이트되었습니다.

이 코드 인쇄 : "초기화에서 호출"

class Init { 
    public void Run() { 
     StandardKernel kernel = new StandardKernel(new MyLoader()); 
     kernel.Get<Tester>(new ConstructorArgument[] { 
      new ConstructorArgument("ClassName", 
       this.GetType().Name 
      ) 
     });    
    } 
} 

public class Tester { 
    public Tester(string ClassName) { 
     Console.WriteLine("Called From {0}", ClassName); 
    } 
} 

편집 :

유용 할 수 있습니다 또 다른 방법은 바인드()를 사용하는 것입니다() WithConstructorArgument() 바인딩에있다.. 자체 바인딩 또는 .WhenInjectedInto() 조건과 함께 사용할 때 유용 할 수 있습니다.

[Inject] 
     public ILog logger { get; set; }  
+0

구문은 다음과 같습니다. (With.Parameters.ConstructorArgument ("name", "value"); 감사합니다.) – JohnRudolfLewis

+0

With.Parameters ... 비트에 대해 확실하지 않습니다. 그건 Ninject 1, Ninject 2는 그런 것 같지 않습니다. –

0

는 ... 여기에 그것을 할 수있는 간단한 방법입니다, 사용자 정의 제공자 생각에 주입 클래스에서 다음

internal class LogModule : StandardModule 
    { 
     private class log4netILogProvider : SimpleProvider<log4net.ILog> 
     { 
      protected override ILog CreateInstance(IContext context) 
      { 
       return LogManager.GetLogger(context.Instance.GetType()); 
      } 
     } 

     public override void Load() 
     { 
      Bind<log4net.ILog>().ToProvider(new log4netILogProvider()); 
     } 
    } 

정교한하려면 원칙 - 추상화는 누가 그것을 사용하는지에 대한 세부 사항에 의존합니다. 이것이 불가피하다면 DI를 사용하여이 의존성을 주입해서는 안됩니다. 아마도 DI 대신에 공장 패턴을 사용하는 것이 더 나은 선택 일 수 있습니까?

관련 문제