2016-12-29 99 views
2

SimpleInjector documentation它是說:SimpleInjector意外行爲

簡單的注射器保留那些從注入IEnumerable<T>ICollection<T>IList<T>IReadOnlyCollection<T>IReadOnlyList<T>實例返回 實例的生活方式。實際上,您應該不會將注入的IEnumerable<T>看作 實例的集合;你應該認爲它是一個實例流。簡單 噴油器將始終注入每次 你迭代IEnumerable<T>,對各個組成部分相同的流(在 IEnumerable<T>ICollection<T>本身是一個單)和參考, 容器被要求解決基於生活方式的實例的 該組件。

看完這個我預期,我已經註冊的所有IFooIFoo<T>爲單身後,IEnumerable<IFoo>將單本身,它枚舉總是會導致相同的對象。

但這段代碼不喜歡的工作:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using SimpleInjector; 

class Program 
{ 
    interface IFoo{} 
    interface IFoo<T> : IFoo{} 
    class Foo : IFoo<Foo>{} 

    class Consumer1 
    { 
     public IFoo<Foo> FooInstance { get; } 
     public Consumer1(IFoo<Foo> fooInstance){FooInstance = fooInstance;} 
    } 

    class Consumer2 
    { 
     public IFoo<Foo> FooInstance { get; } 
     public Consumer2(IFoo<Foo> fooInstance){FooInstance = fooInstance;} 
    } 

    class Consumer3 
    { 
     public IEnumerable<IFoo> FooIntances { get; } 
     public Consumer3(IEnumerable<IFoo> fooIntances){FooIntances = fooIntances;} 
    } 

    static void Main(string[] args) 
    { 
     var cont = new Container(); 

     var types = Assembly.GetExecutingAssembly() 
          .GetTypes() 
          .Where(x => 
            typeof(IFoo<>).IsAssignableFrom(x) || 
            typeof(IFoo).IsAssignableFrom(x)) 
          .Where(x => !x.IsInterface); 

     foreach (var type in types) 
     { 
      var genericFoo = type.GetInterfaces().Single(x=>x.IsGenericType); 
      var genericArgs = genericFoo.GenericTypeArguments; 
      var closedGenericFoo = typeof(IFoo<>).MakeGenericType(genericArgs); 

      var reg = Lifestyle.Singleton.CreateRegistration(type, cont); 

      cont.AddRegistration(closedGenericFoo, reg); 
      cont.AddRegistration(typeof(IFoo), reg); 
     } 

     cont.RegisterCollection<IFoo>(new[] {Assembly.GetExecutingAssembly()}); 

     var cons1 = cont.GetInstance<Consumer1>(); 
     var cons2 = cont.GetInstance<Consumer2>(); 
     var cons3_1 = cont.GetInstance<Consumer3>(); 
     var cons3_2 = cont.GetInstance<Consumer3>(); 

     // Expected: true | Actually: true 
     Console.WriteLine($"cons1.FooInstance == cons2.FooInstance : {cons1.FooInstance == cons2.FooInstance}"); 
     // Expected: true | Actually: true 
     Console.WriteLine($"cons3_1.FooInstances == cons3_2.FooInstances : {cons3_1.FooIntances == cons3_2.FooIntances}"); 
     // Expected: true | Actually: false 
     Console.WriteLine($"cons3_1.FooIntances.First() == cons1.FooInstance : {cons3_1.FooIntances.First() == cons1.FooInstance}"); 
     // Expected: true | Actually: false 
     Console.WriteLine($"cons3_1.FooIntances.First() == cons3_2.FooIntances.First() : {cons3_1.FooIntances.First() == cons3_2.FooIntances.First()}"); 

     Console.ReadKey(); 
    } 
} 

我想要實現:註冊到兩個IFoo<Foo>IFoo(部分

  1. Foo實施作品)
  2. 單一碼流IFoo這是IEnumerable<IFoo>(它的工作原理)
  3. IEnumerable迭代上述應該給我相同的Foo 執行(不工作)

這可能嗎?

+0

請致電'Container.Verify()'在註冊階段的結束。這將導致一個例外,解釋爲什麼你的期望沒有得到滿足。 – Steven

+0

@Steven它不:)它說我有3個'Foo'的註冊:1)單身'IFoo',2)單身'IFOO ',和3)瞬變'IFoo'。第三個從哪裏冒出來。它解釋了爲什麼'IEnumerable '每次創建新的'IFoo',但它不能解釋我怎樣才能避免'IFoo'的臨時註冊 – Szer

回答

2

這裏發生的事情是這樣的:

cont.RegisterCollection<IFoo>(new[] {Assembly.GetExecutingAssembly()})結果如下登記的電話:

cont.RegisterCollection<IFoo>(new[] { typeof(Foo) }); 

當收集解決的第一次,容器將查找登記Foo以確保它將重新使用該註冊並因此重用其生活方式。

隨着您的註冊,但沒有註冊爲Foo可以找到。然而,對於IFoo<Foo>IFoo有註冊,但是簡單注射器涉及哪些是不同的註冊。

因此,Simple Injector將以您的名義爲Foo創建最後一刻註冊,Simple Injector使用的默認生活方式爲Transient

這就是爲什麼雖然您已經註冊Foo兩次(一次爲IFoo<Foo>,一次爲IFoo),但收集的元素仍然是暫時的。

你會注意到這個問題,如果你已在註冊過程中到底叫什麼Container.Verify()。簡單的注射器會爲您檢測到這些錯誤配置。您應該始終致電Container.Verify()

爲了解決這個問題,您可以將您的配置更改爲以下:

static void Main(string[] args) 
{ 
    var container = new Container(); 

    // GetTypesToRegister can do the assembly scanning for you. 
    IEnumerable<Type> types = container.GetTypesToRegister(typeof(IFoo<>), 
     new[] { Assembly.GetExecutingAssembly() }); 

    // Here we create a list of Registration instance once. 
    Registration[] regs = (
     from type in types 
     select Lifestyle.Singleton.CreateRegistration(type, container)) 
     .ToArray(); 

    // Here we register the registrations as one-to-one mapping. 
    foreach (var reg in regs) 
    { 
     Type closedGenericFoo = reg.ImplementationType.GetInterfaces() 
      .Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IFoo<>)) 
      .Single(); 

     container.AddRegistration(closedGenericFoo, reg); 
    } 

    // Here we make a one-to-many mapping between IFoo and the registrations. 
    container.RegisterCollection<IFoo>(regs); 

    container.Verify(); 

    ... 
} 
+0

這是一個很好的答案。但是我不能在我的真實項目中使用'Verify()',因爲'Akka.Net'。在我註冊容器'Verify()'中的所有actor之後,拋出錯誤是因爲它試圖創建actor實例,而'Akka.Net'簡單地不允許在框架之外創建actor。但這是另一回事。 – Szer

+0

@Szer:如果Akka.Net控制着actor的創建,而不是Simple Injector,你應該*不*在Simple Injector中註冊這些actor。您應該[始終確保您的配置可以](https://simpleinjector.readthedocs.io/en/latest/howto.html#verify-configuration)。 – Steven

+0

實際上,SimpleInjector創建了actor,但深深地植入了一些在框架本身注入的'Resolver'。所以我必須明確註冊。 SimpleInjector不能在框架之外創建這些actor實例。 – Szer