2015-09-05 66 views
2

autofac documentation狀態工作:Autofac枚舉不與類型多個註冊或模塊

當Autofac被注入IEnumerable<ITask>類型的構造符參數也不會尋找供給IEnumerable<ITask>的成分。相反,容器會查找ITask的所有實現並注入所有實現。

但實際上,它會將每個註冊類型添加爲已註冊的次數。所以,如果你註冊類兩倍如下:

builder.RegisterType<A>(); 
builder.RegisterType<A>(); 

然後你得到兩個項目枚舉!在單個模塊中,這不是問題,因爲您顯然只需註冊一次您的類型。但是如果你有一個由多個模塊註冊的共享模塊(典型的鑽石模塊依賴關係圖),那麼你可以在枚舉中獲得儘可能多的項目,因爲共享模塊已被其他人註冊...

它是一個錯誤嗎? 有沒有辦法強制枚舉爲每個實現提供單個項目,正如文檔中所述,不再有?

回答

2

您必須看到Autofac作爲一種Dictionary<IComponentRegistration>。 A 註冊可以綁定到類型或委託或其他具有配置行爲的其他位置。

通過註冊登記兩次類型你將有兩個不同的IComponentRegistration,你的情況是相似。如果你看看以下的差異,你可以看到你將有兩個不同的註冊,它們使用相同的Type但具有不同的配置。

builder.RegisterType<A>().WithConstrusctorArgument("param1", "x"); 
builder.RegisterType<A>().WithConstrusctorArgument("param1", "y"); 

在這種情況下解決IEnumerable<A>會給你的A兩個不同的實例,它有一定道理。

有沒有什麼辦法來強制枚舉每個實現

,我不會建議這樣做,提供一個單一的項目。重構你的應用程序可能會更好,不會允許這種情況,如果你需要的話,你可以刪除很多偉大的東西在Autofac。 如果您有一個Type可能註冊在多個模塊中,則在主模塊中註冊Type模塊可能有意義。


順便說一句,如果你真的想這樣做,你將不得不找到一種方法來區分你的容器的所有註冊。然後,您可以通過實施您自己的IRegistrationSource來覆蓋IEnumerable<>的默認實現,下面的示例將爲您提供每種類型的一次註冊。

class CustomEnumerableRegistrationSource : IRegistrationSource 
{ 
    public Boolean IsAdapterForIndividualComponents 
    { 
     get 
     { 
      return false; 
     } 
    } 

    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor) 
    { 
     IServiceWithType typedService = service as IServiceWithType; 

     if (typedService == null) 
     { 
      return Enumerable.Empty<IComponentRegistration>(); 
     } 
     if (!(typedService.ServiceType.IsGenericType && typedService.ServiceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))) 
     { 
      return Enumerable.Empty<IComponentRegistration>(); 
     } 

     Type elementType = typedService.ServiceType.GetGenericArguments()[0]; 

     Service elementService = typedService.ChangeType(elementType); 

     Type collectionType = typeof(List<>).MakeGenericType(elementType); 

     IComponentRegistration registration = RegistrationBuilder.ForDelegate(collectionType, (c, p) => 
     { 
      IEnumerable<IComponentRegistration> registrations = c.ComponentRegistry.RegistrationsFor(elementService); 

      IEnumerable<Object> elements = registrations.Select(cr => c.ResolveComponent(cr, p)); 

      // get distinct elements by type 
      Array array = elements.GroupBy(o => o.GetType()).Select(o => o.First()).ToArray(); 

      Array array2 = Array.CreateInstance(elementType, array.Length); 
      array.CopyTo(array2, 0); 

      Object collection = Activator.CreateInstance(collectionType, new Object[] { array2 }); 

      return collection; 
     }).As(service) 
      .CreateRegistration(); 

     return new IComponentRegistration[] { registration }; 
    } 
} 

而且你可以使用它像這樣:

ContainerBuilder builder = new ContainerBuilder(); 

builder.RegisterType<A1>().As<IA>(); 
builder.RegisterType<A1>().As<IA>(); 
builder.RegisterType<A2>().As<IA>(); 

builder.RegisterSource(new CustomEnumerableRegistrationSource()); 

IContainer container = builder.Build(); 

IEnumerable<IA> services = container.Resolve<IEnumerable<IA>>(); 
Console.WriteLine(services.Count()); 

這個註冊是原始CollectionRegistrationSource的簡化版本。請查看CollectionRegistrationSource.cs的源代碼以獲取更完整的註冊版本。

+1

謝謝!我瞭解現場背後的困境。我不知道類型的註冊不是冪等的。不知何故,你是對的,如果你想在類型和實例之間有一個唯一的註冊行爲,這是有道理的。因此,即使多個模塊依賴於它,模塊也只能被註冊一次。 *所以拇指規則:從不在另一個模塊中註冊模塊!保持一個平坦的模塊模型。只在外部例程中註冊它們,創建容器* – jeromerg