Search code examples
dependency-injectionautofacasp.net-core-3.1

Auto-registration with a RegistrationSource in Autofac


Here's what I'm doing so far (code simplified):

public class MyRegistrationSource : IRegistrationSource
{
    public MyRegistrationSource(ContainerBuilder builder /*,...*/)
    {
       // ...
       this.builder = builder;
    }

    public IEnumerable<IComponentRegistration> RegistrationsFor(
        Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        // Some checks here
        var interfaceType = serviceWithType.ServiceType;
        var implementorType = FindTheRightImplementor(interfaceType);

        if (myRegisterConditionSatisfied) 
        {
            return Register(implementorType, interfaceType);
        }

        return Empty;
    }

    private IEnumerable<IComponentRegistration> Register(Type concrete, Type @interface)
    {
        var regBuilder = builder.RegisterType(concrete).As(@interface).IfNotRegistered(@interface);
        return new[] { regBuilder.CreateRegistration() };
    }
}

Then, at startup I'm doing something like

builder.RegisterSource(
    new NonRegisteredServicesRegistrationSource(builder/*, ...*/));

The above is intended to register those matching services only when there's no previous registration. I tried doing the registration without using the ContainerBuilder but couldn't get it to work.

This is working but are there any issues in passing-in the ContainerBuilder instance to the RegistrationSource?

Thanks!


Solution

  • I'd probably argue against passing in a ContainerBuilder.

    Every type you register in your source will add a callback to a list of callbacks inside the Container Builder which will never get cleared, potentially creating a memory leak.

    I'd suggest calling the static method RegistrationBuilder.ForType instead, which will give you a fluent builder and should let you subsequently call CreateRegistration as you are now.

    You can see some pretty good examples of how do this in our Moq integration:

    var reg = RegistrationBuilder.ForType(concrete)
                                 .As(@interface)
                                 .CreateRegistration();
    

    Also, I don't believe IfNotRegistered will have any effect when used outside the context of a ContainerBuilder. You should use the provided registrationAccessor parameter to the registration source to look up a TypedService to see if it has already been registered:

    var isRegistered = registrationAccessor(new TypedService(@interface)).Any();