2012-11-30 5 views
1

유니티를 처음 접했고 유니티의 오토 팩토리 (Auto Factory) 개념을 확장하는 방법을 알아 내려고 애 쓰고 있습니다. Unity는 매개 변수 자체 대신 Func을 사용하여 팩토리를 생성 할 수있는 기능을 제공합니다. 마찬가지로 :유니티의 자동 추상 팩터

public class Foo 
{ 
    private readonly Func<IBar> _barFactory; 

    public Foo(Func<IBar> barFactory) 
    { 
     _bar = barFactory; 
    } 
} 

사실 공장에서 생성 한 클래스에는 매개 변수가 필요하며 런타임에는 매개 변수 만 알 수있는 곳이 있습니다. 그 외에도 매개 변수의 수와 유형은 클래스마다 다릅니다.

내가 찾고있는 것은 Autofac DelegateFacotry과 비슷하지만 Unity의 느낌을 유지하고 싶습니다. 유니티가 생성자와 함께 작동 않기 때문에

public class Foo 
{ 
    private readonly Func<int, string, IBar> _barFactory; 

    public Foo(Func<int, string, IBar> barFactory) 
    { 
     _bar = barFactory; 
    } 
} 

위의 코드는 작동하지 않지만, 내가 찾고 정확히 무엇을 : 그래서, 유니티 그런 일을 할 수 있도록합니다.

BuilderStrategy를 사용해 보았지만 표현식 또는 일리노이 생성으로 귀결됩니다. 그 길을 가기 전에 다른 옵션을 확인하고 싶습니다.

나를 도와 줄 수있는 Unity와 충분한 경험을 가진 사람이 있습니까? 여기

는 제약을하다 내가 가진 :

  1. 컨테이너 내에서 모든 단일 Func을 등록 할 필요가 없습니다 Func을
  2. 사용하는 유니티의 개념을 유지
  3. 생성자 Func을에서 Func을에 Func을을 (받아 들여야한다 Func을 내가 클레아했다 희망 컨테이너를 변경
  4. 가 옵션

아니다) 이미 처리 충분히 r. 내가 아니었다면 알려주십시오.

편집 1 : 추상 공장을 직접 사용할 수 있음을 알고 있습니다. 그러나 첫 번째 목표는 유니티의 느낌을 유지하는 것입니다.

+0

어려운 제약 집합입니다 ... 사용자 지정 대리자를 사용하면 매개 변수 이름 일치를 수행하고 매개 변수 재정의를 사용할 수 있습니다 (http://msdn.microsoft.com/en-us/library/ff660920(v=pandp) .20) .aspx # injection_paramoverride) 재정의를 사용하여 컨테이너에 다시 호출하는 함수를 만듭니다. 귀하의 예제에서는 Func에서 인터페이스를 반환하기를 원합니다.이 인터페이스는 인터페이스 구현을 가져올 수 있으므로 솔루션을 더욱 복잡하게 만듭니다. – fsimonazzi

+0

나는 당신의 요점을 fsimonazzi를 참조하십시오. 그러나 그런 식으로 추상 팩토리를 사용하거나 필요한 유형을 등록하는 것이 더 간단합니다. 내가 정말로 이루고자하는 것은 너무 많은 설정없이 Unity의 방법을 유지하는 것입니다. 이상적으로는 컨테이너의 호출을 작성하여 Unity가 나머지를 파악하도록 할 것입니다. – Allan

답변

1

나는 내 자신의 질문에 답하고 싶지 않지만 어떻게 든 문제를 해결할 수 있었고 다른 사람들이 관심을 가질만한 솔루션이라고 생각합니다. 유니티의 소스 코드를보고 거기서 기본적인 아이디어를 얻었습니다. 또한 Unity와 관련하여 몇 가지 게시물을 읽었습니다. 여기에 :

우선, IBuildPlanPolicy에서 상속받은 클래스를 만들어야했습니다. 클래스 내에서 몇 가지 지원 클래스를 남겨 두었 기 때문에 길었습니다.

public class AutomaticFactoryBuilderPolicy : IBuildPlanPolicy 
{ 
    private readonly Dictionary<Type, Type> _callables = 
     new Dictionary<Type, Type> 
      { 
       {typeof(Func<,>), typeof(CallableType<,>)}, 
       {typeof(Func<,,>), typeof(CallableType<,,>)}, 
       {typeof(Func<,,,>), typeof(CallableType<,,,>)}, 
       {typeof(Func<,,,,>), typeof(CallableType<,,,,>)} 
      }; 

    public void BuildUp(IBuilderContext context) 
    { 
     if (context.Existing == null) 
     { 
      var currentContainer = context.NewBuildUp<IUnityContainer>(); 
      var buildKey = context.BuildKey; 

      string nameToBuild = buildKey.Name; 

      context.Existing = CreateResolver(currentContainer, buildKey.Type, nameToBuild); 
     } 
    } 

    private Delegate CreateResolver(IUnityContainer currentContainer, 
     Type typeToBuild, string nameToBuild) 
    { 
     Type[] delegateTypes = typeToBuild.GetGenericArguments(); 
     Type func = typeToBuild.GetGenericTypeDefinition(); 
     Type callable = _callables[func]; 

     Type callableType = callable.MakeGenericType(delegateTypes); 
     Type delegateType = func.MakeGenericType(delegateTypes); 
     MethodInfo resolveMethod = callableType.GetMethod("Resolve"); 

     object callableObject = Activator.CreateInstance(callableType, currentContainer, nameToBuild); 
     return Delegate.CreateDelegate(delegateType, callableObject, resolveMethod); 
    } 

    private class CallableType<T1, TResult> 
    { 
     private readonly IUnityContainer _container; 
     private readonly string _name; 

     public CallableType(IUnityContainer container, string name) 
     { 
      _container = container; 
      _name = name; 
     } 

     public TResult Resolve(T1 p1) 
     { 
      return _container.Resolve<TResult>(_name, new OrderedParametersOverride(new object[] { p1 })); 
     } 
    } 

    private class CallableType<T1, T2, TResult> 
    { 
     private readonly IUnityContainer _container; 
     private readonly string _name; 

     public CallableType(IUnityContainer container, string name) 
     { 
      _container = container; 
      _name = name; 
     } 

     public TResult Resolve(T1 p1, T2 p2) 
     { 
      return _container.Resolve<TResult>(_name, new OrderedParametersOverride(new object[] { p1, p2 })); 
     } 
    } 

    private class CallableType<T1, T2, T3, TResult> 
    { 
     private readonly IUnityContainer _container; 
     private readonly string _name; 

     public CallableType(IUnityContainer container, string name) 
     { 
      _container = container; 
      _name = name; 
     } 

     public TResult Resolve(T1 p1, T2 p2, T3 p3) 
     { 
      return _container.Resolve<TResult>(_name, new OrderedParametersOverride(new object[] { p1, p2, p3 })); 
     } 
    } 

    private class CallableType<T1, T2, T3, T4, TResult> 
    { 
     private readonly IUnityContainer _container; 
     private readonly string _name; 

     public CallableType(IUnityContainer container, string name) 
     { 
      _container = container; 
      _name = name; 
     } 

     public TResult Resolve(T1 p1, T2 p2, T3 p3, T4 p4) 
     { 
      return _container.Resolve<TResult>(_name, new OrderedParametersResolverOverride(new object[] { p1, p2, p3, p4 })); 
     } 
    } 

} 

매우 간단합니다. 트릭은 각각 Func에 대해 하나의 CallableType을 생성하는 것입니다. 제가 원했던만큼 역동적이지는 않습니다. 그러나 역동적 인 것으로 만들기 위해서 IL이나 Expression Trees 중 하나를 다루어야한다고 생각합니다. 내가 가지고있는 방식은 지금 나를 위해 충분하다.

둘째, Unity는 매개 변수를 이름으로 처리하지만 주문으로 처리해야했습니다. OrderedParametersResolverOverride가 작동하는 곳입니다 (이 클래스는 위의 코드에서 사용됩니다).) CallableType 클래스를 확인하십시오

public class OrderedParametersResolverOverride : ResolverOverride 
{ 
    private readonly Queue<InjectionParameterValue> _parameterValues; 

    public OrderedParametersResolverOverride(IEnumerable<object> parameterValues) 
    { 
     _parameterValues = new Queue<InjectionParameterValue>(); 
     foreach (var parameterValue in parameterValues) 
     { 
      _parameterValues.Enqueue(InjectionParameterValue.ToParameter(parameterValue)); 
     } 
    } 

    public override IDependencyResolverPolicy GetResolver(IBuilderContext context, Type dependencyType) 
    { 
     if (_parameterValues.Count < 1) 
      return null; 

     var value = _parameterValues.Dequeue(); 
     return value.GetResolverPolicy(dependencyType); 
    } 
} 

이 두 클래스는 Func 생성 처리합니다. 다음 단계는 그 빌더를 Unity의 파이프 라인에 추가하는 것입니다. 우리는 UnityContainerExtension을 만들어야합니다 :

public class AutomaticFactoryExtension: UnityContainerExtension 
{ 
    protected override void Initialize() 
    { 
     var automaticFactoryBuilderPolicy = new AutomaticFactoryBuilderPolicy(); 

     Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy), 
      automaticFactoryBuilderPolicy, 
      new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,>))); 

     Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy), 
      automaticFactoryBuilderPolicy, 
      new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,,>))); 

     Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy), 
      automaticFactoryBuilderPolicy, 
      new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,,,>))); 

     Context.Policies.Set(typeof(Microsoft.Practices.ObjectBuilder2.IBuildPlanPolicy), 
      automaticFactoryBuilderPolicy, 
      new Microsoft.Practices.ObjectBuilder2.NamedTypeBuildKey(typeof(Func<,,,,>))); 
    } 
} 

마지막 부분은 실제로 유니티의 파이프 라인에 클래스를 추가하는 것입니다

IUnityContainer container = new UnityContainer(); 
container.AddExtension(new AutomaticFactoryExtension()); 

등록의 나머지 부분은 표준입니다.

이제 생성자는 Func<>에서 Fun<,,,,>까지 가능합니다. 다음 생성자는, 예를 들어, 지금은 (는 IFoo를 해결 할 수 있습니다 가정) 처리됩니다

public class Bar 
{ 
    private readonly Func<int, string, IFoo> _fooFactory; 

    public Bar(Func<int, string, IFoo> fooFactory) 
    { 
     _fooFactory = fooFactory; 
    } 
} 

어떤 질문이 있으면 알려주세요.

희망이 도움이됩니다.

+1

그게 좋은 해결책이지만, 확실하게 Func의 서명과 일치하는 특정 ctor를 선택해야하며, 일치하지 않으면 런타임에 오류가 발생합니다. InjectionParameterValue.ToParameter는 Type 인스턴스를 종속성을 해결하기위한 요청으로 변경합니다. 따라서 Type을 ctor 매개 변수로 전달해야하는 경우 InjectionParameter 에 래핑해야하며이 값은 사용자가 해결하는 함수의 서명에 있어야합니다. – fsimonazzi

+0

예, 생성자 제한을 이해합니다. 그러나 내 문제를 해결하기에 충분합니다. 유형 제한에 대해 알지 못했습니다. 형식을 생성자 인수로 사용하지 않아도됩니다 (지금까지). 어쨌든, 그 문제를 다루기위한 코드 예제가 있습니까? – Allan

+0

다음과 같은 경우 : 공용 인터페이스 IFoo { void DoSomething(); } 공용 클래스 Bar : IFoo { private int theInt; 개인 유형 theType; 공용 막대 (int int, 유형 theType) { this.theInt = theInt; this.theType = theType; } 공개 무효 DoSomething() { Console.WriteLine (this.theInt + ""이 + this.theType.이름); } } – fsimonazzi

0

위임자를 생성하고 유니티에 등록하고 인스턴스를 등록하는 것에 대해 생각해 보셨습니까? 그런 식으로 매개 변수와 매개 변수에 대한 설명과 대리인 자체의 이름을 지정했습니다. 그런 다음 빌드 정책을 만들 필요가 없으며 코드를보다 쉽게 ​​읽을 수 있습니다.