Victor P
Victor P

Reputation: 1612

Autofac registration resolved by method

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

Answers (2)

Cyril Durand
Cyril Durand

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

Tamas
Tamas

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

Related Questions