2015-11-24 73 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

这是不好的解决方案。每次您将创建两个实例 - 默认情况下为第一个实例,第二个实例为您的新实例。 –