Search code examples
c#dependency-injectionautofacautofac-configuration

Autofac register all of type IFoo named IFoo.Name


Just learning Autofac and struggling to rgister a handful of named instances by convention.

public interface IFoo
{
    string AutoFactName{ get; }
    object DoSomething();
}

looking at this interface what i am trying to accomplish is something along these lines

        builder.RegisterTypes()
            .AssignableTo<IFoo>()
            .As<IFoo>()
            .Named<IFoo>(i => i.AutoFactName);

I have tried a few variations of something to this effect. the end goal is to dynamically register and resolve instances.


Solution

  • You should not need instances to register your types in Autofac. If you need information from your type it is better to use meta information like Attribute. Something like that :

    [FooMetadata("Foo1")]
    public class Foo1 : IFoo 
    { }
    

    and then use this metadata while registering

    builder.RegisterAssemblyTypes(assembly)
            .AssignableTo<IFoo>()
            .Named<IFoo>(t => t.GetCustomAttribute<FooMetadata>().Foo);
    

    If you rely need to get the named type in your instance you can do it with a IRegistrationSource

    public class NamedFooRegistrationSource : IRegistrationSource
    {
        public bool IsAdapterForIndividualComponents => false;
    
        public IEnumerable<IComponentRegistration> RegistrationsFor(
            Service service,
            Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
        {
    
            KeyedService keyedService = service as KeyedService;
    
            if (keyedService == null || keyedService.ServiceKey.GetType() != typeof(String))
            {
                yield break;
            }
    
            IComponentRegistration registration = RegistrationBuilder
                .ForDelegate(keyedService.ServiceType, (c, p) =>
                {
                    Type foosType = typeof(IEnumerable<>).MakeGenericType(keyedService.ServiceType);
                    IEnumerable<IFoo> foos = (IEnumerable<IFoo>)c.Resolve(foosType);
    
                    foos = foos.Where(f => f.AutoFactName == (String)keyedService.ServiceKey).ToArray();
                    if (foos.Count() == 0)
                    {
                        throw new Exception($"no Foo available for {keyedService.ServiceKey}");
                    }
                    else if (foos.Count() > 1)
                    {
                        throw new Exception($"more than 1 Foo available for {keyedService.ServiceKey}");
                    }
                    else
                    {
                        return foos.First();
                    }
                })
                .Named((String)keyedService.ServiceKey, keyedService.ServiceType)
                .CreateRegistration();
    
            yield return registration;
        }
    }
    

    and then register your Foo this way :

    builder.RegisterAssemblyTypes(assembly)
            .AssignableTo<IFoo>()
            .As<IFoo>();
    builder.RegisterSource<NamedFooRegistrationSource>();