1
votes

I'm building core services for large app and we switched from castle windsor to autofac. Everything is working fine in automatic registration approch using assemly scanning and modules but we have latelly interesting case: there were 2 implementations of one interface and autofac of course register them in random order that wrong implementation was resolved from business point of view. Of course manual registration do the job but it would be nice to have some validation on IContainer level to at least give as output information about every single registered service and if it has more then one implementation it should mark them which is default - which will be resolved when asking container using resolve. The purpose is to force developers to register specific concrete implementation when there are available multiple but maintain auto registration.
Have u found a way to this case?

1
Today i asked same queation on github developers that maintains code but the response is "it will not be included in base api because it will have impact for so many core classes" :( so there is a hint for you and there are at least 2 approches to build simple solution: one using reflection and accessing private structures and fields and other using official api. - Macko
Do whatcha gotta do, as long as you understand the info you get out may or may not be accurate for the reasons described in Autofac issue #853. - Travis Illig
Thanks Travis, I've already prepared some nice and easy extension method to IContainer that simply count registrations and for simple registrations it looks like it's working. I'll let my code work for a while and see if it manages more complex registrations. - Macko

1 Answers

1
votes
public static class AutofacExtensions
{
    /// <summary>
    /// Checks if one implementation was chosen by developer for every interface
    /// </summary>
    /// <param name="container"></param>
    public static void ValidateRegistrations(this IContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException(nameof(container));
        }

        var registrations = GetRegistrations(container);
        Validate(registrations);
    }

    static void Validate(IDictionary<Type, IList<Type>> registrations)
    {
        foreach (var registration in registrations.OrderBy(x => x.Key.FullName))
        {
            var unique = registration.Value.Distinct().Count();

            if (unique > 1)
            {
                var all = registration.Value.Count;

                if (all <= unique)
                {
                    var currentImplementations = registration.Value.Distinct().Select(x => x.FullName);
                    var aggregatedImplementations = string.Join(", ", currentImplementations);

                    throw new InvalidOperationException($"IoC/DI: for type '{registration.Key.FullName}' was not chose default implementation! Registered available implementations: {aggregatedImplementations}");
                }
            }
        }
    }

    static IDictionary<Type, IList<Type>> GetRegistrations(IContainer container)
    {
        var registrations = new Dictionary<Type, IList<Type>>();

        foreach (var registration in container.ComponentRegistry.Registrations)
        {
            foreach (var service in registration.Services.OfType<IServiceWithType>().Where(x => x.ServiceType.Namespace != null && x.ServiceType.Namespace.ToLowerInvariant().StartsWith("flb.")))
            {
                IList<Type> implementations;

                if (!registrations.TryGetValue(service.ServiceType, out implementations))
                {
                    implementations = new List<Type>();
                    registrations[service.ServiceType] = implementations;
                }

                implementations.Add(registration.Activator.LimitType);

            }
        }

        return registrations;
    }

}