2015-11-24 71 views
0

我使用的是CacheManager,它使用工廠來創建實例。我不想註冊我聲明的每種類型的ICacheManager,所以我正在尋找一種方法來使用動態解析方法註冊泛型類型。使用Ninject,我可以這樣做:通過方法解決的Autofac註冊

kernel 
    .Bind(typeof(ICacheManager<>)) 
    .ToMethod((context) => CacheFactory.FromConfiguration(context.GenericArguments[0], "defaultCache")) 
    .InSingletonScope(); 

其中context.GenericArguments [0]將是我的通用類型。例如,來自

的對象
ICacheManager<object> cache; 

如何使用Autofac做類似這樣的事情?

編輯

基於以下西里爾的回答工作IRegistrationSource。由於CacheManager使用CacheManager實例ID預先添加密鑰,因此SingleInstance是必需的。

public class CacheManagerRegistrationSource : 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 || !typedService.ServiceType.IsGenericType) 
     { 
      yield break; 
     } 

     Type cacheManagerType = typedService.ServiceType; 
     if (cacheManagerType.GetGenericTypeDefinition() != typeof(ICacheManager<>)) 
     { 
      yield break; 
     } 

     IComponentRegistration registration = (IComponentRegistration)RegistrationBuilder.ForDelegate(cacheManagerType, (c, p) => 
     { 
      return CacheFactory.FromConfiguration(cacheManagerType.GetGenericArguments()[0], "defaultCache"); 
     }) 
     .SingleInstance() 
     .CreateRegistration(); 

     yield return registration; 
    } 
} 

回答

1

默認情況下Autofac不允許檢索正在註冊的通用類型的對象。有沒有簡單的內置的方式做到這一點,但你可以使用IRegistrationSource達到你想要的東西:

public class CacheManagerRegistrationSource : 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) 
     { 
      yield break; 
     } 

     Type cacheManagerType = typedService.ServiceType 
              .GetInterfaces() 
              .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICacheManager<>)) 
              .FirstOrDefault(); 

     if (cacheManagerType == null) 
     { 
      yield break; 
     } 

     IComponentRegistration registration = 
      RegistrationBuilder.ForDelegate(cacheManagerType, (c, p) => { 
       return CacheFactory.FromConfiguration(cacheManagerType.GetGenericArguments()[0], "defaultCache"); 
      }) 
      .SingleInstance() 
      .CreateRegistration(); 

     yield return registration; 
    } 
} 

那麼你將不得不做註冊登記來源:

builder.RegisterSource(new CacheManagerRegistrationSource()); 

RegisterGeneric方法是內部工作的方式。

+0

謝謝。你的答案几乎是現貨。我已經在我的問題中發佈了工作代碼。 –

0

這是可怕的,但你可以做到以下幾點:

var builder = new ContainerBuilder(); 
builder.RegisterGeneric(typeof(ICacheManager<>)) 
    .OnActivating(e => 
    { 
     var instance = typeof(CacheFactory).GetMethod("FromConfiguration").MakeGenericMethod 
      e.Instance.GetType().GenericTypeArguments[0]) 
      .Invoke(null, new object[] { "..." }); 
     e.ReplaceInstance(instance); 
    }); 
+0

當我嘗試獲取此錯誤消息:在Autofac.dll中發生類型'System.InvalidOperationException'的異常,但未在用戶代碼中處理 其他信息:類型'CacheManager.Core.ICacheManager'1'沒有實現接口'CacheManager.Core.ICacheManager'1。 –

+0

哦,我明白爲什麼。我不小心用下面的模擬聲明'public class ICacheManager {}'測試了這段代碼。請注意,它不是一個接口,而是一個類。 – Tamas

+0

這是不好的解決方案。每次您將創建兩個實例 - 默認情況下爲第一個實例,第二個實例爲您的新實例。 –