2016-09-13 4 views
1

에서 일반적인 인터페이스를 해결하는 일반적인 인터페이스 :Autofac : 일반적인 방법

public interface IGeneric<T>{} 

이 클라이언트는 :

public class ClientClass 
{ 
    public void DoSomething<T>() 
    { 
     //what to inject in constructor 
     //to get an implementation of the IGeneric<T> from autofac? 
    } 
} 

어떤 생각 방법이 작업을 수행하려면?

+0

@downvoter, 의견이 있으십니까? –

+0

인터페이스 인스턴스를 만들 수 없습니다. 이 인터페이스를 구현하는 클래스가 필요합니다. 따라서 인터페이스가 발견되면 해당 클래스를 * 사용하도록 컨테이너를 구성해야합니다. 그렇지 않습니까? 아마도 이것은 ypu 도움이 될 수 있습니다 : http://stackoverflow.com/questions/1189519/resolving-generic-interface-with-autofac – HimBromBeere

+0

ypur 질문 많은 노력을 보여주지 않기 때문에 downvote 아마도 등장했다. 당신의 질문은 * 인스턴스를 얻거나 * 클래스를 인터페이스에 등록 *하고 있습니까? – HimBromBeere

답변

1

당신이 당신의 DoSomething 방법이처럼 해결할 수 있습니다 당신은 당신의 인터페이스

public class MyGeneric<T> : IGeneric<T> 
{ 
} 

를 구현하는 클래스를 가정하고 당신은 당신의 컨테이너 다음

builder.RegisterGeneric(typeof(MyGeneric<>)).As(typeof(IGeneric<>)); 

을에 등록하고있는

public class ClientClass 
{ 
    private readonly ILifetimeScope _scope; 

    public ClientClass(ILifetimeScope scope) 
    { 
     _scope = scope; 
    } 

    public void DoSomething<T>() 
    { 
     var myGeneric = _scope.Resolve<IGeneric<T>>(); 
    } 
} 

당신이 볼 수 있듯이 Autofac 스코프 (ILifetimeScope)을 사용하는 것이 바람직하다. 생성자를 사용하여 삽입 할 수 있습니다. 내가 아는 한 다른 방법은 없습니다. ClientClass은 제네릭 자체가 아니기 때문입니다. ClientClass 인스턴스를 만들 때 T 유형을 모르기 때문에 생성자 또는 속성 주입을 사용하여 IGeneric<T> 인스턴스를 가져올 수 없습니다. (위 그림 참조)

  • 당신의 ClientClass로 범위를 주입하고 IGeneric<T> 인스턴스를 해결하는 데 사용할
  • ClientClass 일반을 확인하고 IGeneric<T> 인스턴스를 주입 : 제가보기에는

    은 두 가지 옵션이 있습니다 생성자에서

+2

여기서 중요한 것은 컴포지션 루트의 일부인 클래스에서 'ILifetimeScope'인스턴스 만 injecy시켜야한다는 것입니다 (나머지 응용 프로그램이 컨테이너에 종속되지 않도록하기 위해). 즉, 'ClientClass'는 추상화를 구현해야합니다. 이 'IClientClass' 추상화는 응용 프로그램의 중심에 위치 할 수 있으며 다른 코드로 참조 할 수 있습니다. 반면'ClientClass' 구현은 Composition Root 내에 배치 될 수 있습니다. – Steven

0

나는이 동일한 문제가 있었고, 나는 해결책을 취했다. 다음 클래스를 고려

public class InjectionInterceptor : IInterceptor { 
    private readonly ILifetimeScope _Scope; 
    public InjectionInterceptor(ILifetimeScope scope) { 
     _Scope = scope; 
    } 

    public void Intercept(IInvocation invocation) { 
     using (var lifetime = _Scope.BeginLifetimeScope(string.Format("InjectionInterceptor {0}", Guid.NewGuid()))) { 
      InjectDependencyIfNecessary(invocation, lifetime); 
      invocation.Proceed(); 
     } 
    } 

    private void InjectDependencyIfNecessary(IInvocation invocation, ILifetimeScope lifetime) { 
     int indexOfDependencyArgument = FindDependencyArgument(invocation.Method); 
     if (indexOfDependencyArgument >= 0 && invocation.GetArgumentValue(indexOfDependencyArgument) == null) { 
      SetDependencyArgument(invocation, indexOfDependencyArgument, lifetime); 
     } 
    } 

    private static int FindDependencyArgument(System.Reflection.MethodInfo method) { 
     var allArgs = method.GetParameters(); 
     return Array.FindIndex(allArgs, param => 
      param.ParameterType.IsInterface && 
      param.ParameterType.IsGenericType && 
      param.ParameterType.GetGenericTypeDefinition() == typeof(IGeneric<>)); 
    } 

    private void SetDependencyArgument(IInvocation invocation, int indexOfDependencyArgument, ILifetimeScope lifetime) { 
     var methodArg = invocation.Method.GetGenericArguments().Single(); 
     var dependency = lifetime.Resolve(typeof(IGeneric<>).MakeGenericType(methodArg)); 
     invocation.SetArgumentValue(indexOfDependencyArgument, dependency); 
    } 
} 

클라이언트 클래스는이 클래스에 의해 차단되는 등록

var builder = new ContainerBuilder(); 
builder.RegisterType<InjectionInterceptor>(); 
builder.RegisterType<ClientClass>() 
    .EnableClassInterceptors() 
    .InterceptedBy(typeof(InjectionInterceptor)); 
이 IGeneric의 인스턴스를 허용하도록 방법을 변경

: 가정

public class ClientClass 
{ 
    public virtual void DoSomething<T>(IGeneric<T> dependency = null) //must be virtual to be intercepted 
    { 
     if (dependency == null) throw new ArgumentNullException(nameof(dependency)); 
     //use dependency here 
    } 
} 

당신의 ClientClass가 Autofac에 의해 해결되면, (가상으로 표시된) 모든 메소드가이 클래스에 의해 인터셉트됩니다. 메소드 인수를 검사하고 IGeneric 인 메소드를 찾으려고 시도합니다. 전달 된 인수가 null이면 호출 된 메서드의 제네릭 형식 매개 변수를 검사하고 IGeneric의 인스턴스를 확인합니다. 그런 다음 인수를 해당 확인 된 값으로 설정합니다.

당신은 종속 기본 매개 변수 필요가 없습니다,하지만 당신은 일반적으로이하는 것처럼 원하는 경우 여전히 당신에게 특정 유형을 주입 할 수있는 옵션을 제공하는 동안, 당신은 당신의 메소드를 호출 할 수 있습니다 :

client.DoSomething<int>(); //injected by the interceptor 
client.DoSomething(new Generic<int>()); // resolved manually; interceptor does nothing 

이 메소드에 대한 한 가지 큰 단점은 빈 인자 목록을 사용하여 DoSomething() 메소드를 호출하는 경우 코드에서 작업하거나 디버깅하는 다른 사람들에게 이해하기가 어렵다는 것입니다.