Reputation: 1612
I'm using CacheManager and it uses a factory to create instances. I don't want to have to register every type of ICacheManager that I declare so I'm looking for a way to register the generic type with a dynamic resolve method. Using Ninject, I could do this:
kernel
.Bind(typeof(ICacheManager<>))
.ToMethod((context) => CacheFactory.FromConfiguration(context.GenericArguments[0], "defaultCache"))
.InSingletonScope();
where context.GenericArguments[0] would be the type of my generic. For example, object from
ICacheManager<object> cache;
How do I do something like this using Autofac?
Edit
Working IRegistrationSource based on Cyril's answer below. SingleInstance is required since CacheManager prepends keys with the CacheManager instance id.
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;
}
}
Upvotes: 2
Views: 829
Reputation: 16192
By default Autofac doesn't allow to retrieve the generic type of object being registered. There is no easy built-in way to do this but you can use a IRegistrationSource
to achieve what you want :
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;
}
}
then you will have to register the registration source by doing :
builder.RegisterSource(new CacheManagerRegistrationSource());
This is the way the RegisterGeneric
method works internally.
Upvotes: 2
Reputation: 6420
It's awful, but you can do the following:
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);
});
Upvotes: 0