2016-01-21 26 views
1

Unity Container中有很多註冊。事實上長約800行。由於Unity沒有驗證方法,因此我們在測試應用程序之前引入了單元測試來驗證配置,該應用程序看起來像下面這樣工作得很好,並且在配置中遇到了很多問題:在.NET中查看Unity依賴關係配置

IUnityContainer container = UnityContainerBuilder.Build(); 
foreach (ContainerRegistration mapping in container.Registrations) 
{ 
    container.Resolve(mapping.RegisteredType, mapping.Name); 
} 

但是,我們有很多類在decorator pattern之後,在那裏我們使用命名的註冊。這些命名註冊,然後用來建立的InjectionConstructor其他註冊,例如:

container.RegisterType<IMyType2, Concrete1MyType2>("Concrete1MyType2", new ContainerControlledLifetimeManager()); 
container.RegisterType<IMyType2, Concrete2MyType2>("Concrete2MyType2", new ContainerControlledLifetimeManager()); 
container.RegisterType<IMyType1, Concrete1MyType1>(
    "Concrete1MyType1", 
    new ContainerControlledLifetimeManager(), 
    new InjectionConstructor(
     new ResolvedParameter<IMyType2>("Concrete2MyType2"))); 

是否有可能通過一些代碼,你在InjectionConstructor的已配置您的註冊訪問?

回答

0

This article似乎建議你可以。

給出的例子是:

void DisplayContainerRegistrations(IUnityContainer theContainer) 
{ 
    string regName, regType, mapTo, lifetime; 
    Console.WriteLine("Container has {0} Registrations:", 
      theContainer.Registrations.Count()); 
    foreach (ContainerRegistration item in theContainer.Registrations) 
    { 
    regType = item.RegisteredType.Name; 
    mapTo = item.MappedToType.Name; 
    regName = item.Name ?? "[default]"; 
    lifetime = item.LifetimeManagerType.Name; 
    if (mapTo != regType) 
    { 
     mapTo = " -> " + mapTo; 
    } 
    else 
    { 
     mapTo = string.Empty; 
    } 
    lifetime = lifetime.Substring(0, lifetime.Length - "LifetimeManager".Length); 
    Console.WriteLine("+ {0}{1} '{2}' {3}", regType, mapTo, regName, lifetime); 
    } 
} 
+0

@Nikolia - 我遇到過那一個。它基本上顯示了你有什麼類型的映射和終身管理器。我想知道我用InjectionConstructors配置了什麼。這篇文章沒有涉及。 – Andez

0

OK,在這個沒有得到很多信息後,我看了一下團結內部,因爲我看不到這樣做的任何公開方式。我提出了以下使用Reflection來訪問Unity各個部分的私有成員。

這樣做的主要原因是將瞬態實例注入到單例中是有點不匹配的。瞬間應該只能持續很短的時間,而單身可能在DI容器的整個生命週期中保持不變。關於簡單注射器here有一些很好的信息。這是嘗試獲取注入信息的原因之一,因爲Unity沒有像簡單注入器那樣的驗證。

所以,這裏是我正在註冊的服務接口和類。有3個接口和3個具體實現,但有2個實現的IService2除外。

public class Service1 : IService1 
{ 
    private readonly IService2 _service2; 
    private readonly IService3 _service3; 

    public Service1(IService2 service2, IService3 service3) 
    { 
     _service2 = service2; 
     _service3 = service3; 
    } 

    public int DoSomethingForService1() 
    { 
     return 1; 
    } 
} 

public interface IService1 
{ 
    int DoSomethingForService1(); 
} 

public class Service2 : IService2 
{ 
    public int DoSomethingForService2() 
    { 
     return 1; 
    } 
} 

public class Service3 : IService3 
{ 
    public int DoSomethingForService3() 
    { 
     return 1; 
    } 
} 

public interface IService3 
{ 
    int DoSomethingForService3(); 
} 

public class Service2_1 : IService2 
{ 
    public int DoSomethingForService2() 
    { 
     return 1; 
    } 
} 

public interface IService2 
{ 
    int DoSomethingForService2(); 
} 

現在Unity容器已經建成。我們傾向於爲此使用單獨的項目,因爲我們不希望所有引用都保存在UI中。我們正在使用2個名爲IService2的註冊。

public static class UnityContainerBuilder 
{ 
    public static IUnityContainer BuildDirectInUnity() 
    { 
     IUnityContainer container = new UnityContainer(); 

     container.RegisterType<IService2, Service2>("Service2OneAndOnly", new ContainerControlledLifetimeManager()); 
     container.RegisterType<IService2, Service2_1>("Service2_1", new ContainerControlledLifetimeManager()); 
     container.RegisterType<IService3, Service3>(new ContainerControlledLifetimeManager()); 
     container.RegisterType<IService1, Service1>(new ContainerControlledLifetimeManager(), 
      new InjectionConstructor(
       new ResolvedParameter<IService2>("Service2OneAndOnly"), 
       new ResolvedParameter<IService3>())); 
    } 
} 

現在有趣的部分。在這裏,我們得到了統一配置:

static void Main(string[] args) 
{ 
    Console.WriteLine("Building Unity Container..."); 
    var container = (UnityContainer)UnityContainerBuilder.BuildDirectInUnity(); 

    Console.WriteLine("Listing Registrations..."); 

    FieldInfo policiesField = typeof(UnityContainer).GetFields(
        BindingFlags.NonPublic | 
        BindingFlags.Instance).First(f => f.Name == "policies"); 

    FieldInfo parameterValuesField = typeof(SpecifiedConstructorSelectorPolicy).GetFields(
        BindingFlags.NonPublic | 
        BindingFlags.Instance).First(f => f.Name == "parameterValues"); 

    FieldInfo paramNameField = typeof(ResolvedParameter).GetFields(
        BindingFlags.NonPublic | 
        BindingFlags.Instance).First(f => f.Name == "name"); 

    var policies = (PolicyList)policiesField.GetValue(container); 

    // build up a dictionary for loop below to use to get the lifetime manager 
    var typeToRegistration = new Dictionary<Tuple<Type, string>, ContainerRegistration>(); 

    foreach (ContainerRegistration registration in container.Registrations) 
    { 
     typeToRegistration.Add(new Tuple<Type, string>(registration.RegisteredType, registration.Name), registration); 
    } 

    // now output the list 
    foreach (ContainerRegistration registration in container.Registrations) 
    { 
     Console.WriteLine("{0} to {1}, {2}, {3}", 
      registration.RegisteredType.Name, 
      registration.MappedToType.Name, 
      registration.Name ?? "[default]", 
      registration.LifetimeManagerType.Name); 

     // need to check for our InjectionConstructor - I need local = false 
     IConstructorSelectorPolicy constructorPolicy = policies.Get<IConstructorSelectorPolicy>(
      new NamedTypeBuildKey(registration.MappedToType, registration.Name), false); 

     // and I need SpecifiedConstructorSelectorPolicy as we are not using the default constructor 
     if (constructorPolicy is SpecifiedConstructorSelectorPolicy) 
     { 
      var specifiedConstructorPolicy = constructorPolicy as SpecifiedConstructorSelectorPolicy; 

      // now output the ResolvedParameters for type, name, lifetime manager 
      var paramValues = (InjectionParameterValue[])parameterValuesField.GetValue(specifiedConstructorPolicy); 

      foreach (var param in paramValues) 
      { 
       if (param is ResolvedParameter) 
       { 
        var resolvedParam = param as ResolvedParameter; 

        var name = (string)paramNameField.GetValue(resolvedParam); 
        string lifeTimeManagerName = 
         typeToRegistration[new Tuple<Type, string>(resolvedParam.ParameterType, name)].LifetimeManagerType.Name; 

        Console.WriteLine("\t{0}, {1}, {2}", param.ParameterTypeName, name ?? "[default]", lifeTimeManagerName); 
       } 
       else 
       { 
        Console.WriteLine("\t{0}", param.ParameterTypeName); 
       } 
      } 
     } 
    } 

    Console.WriteLine("Complete"); 
    Console.ReadLine(); 
} 

這樣做的缺點是,它是純粹基於反射黑客攻擊,它不支持InjectionFactory在那裏你可以配置類型的新實例。