Macko
Macko

Reputation: 966

Autofac registrations validation

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?

Upvotes: 1

Views: 1204

Answers (1)

Macko
Macko

Reputation: 966

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;
    }

}

Upvotes: 1

Related Questions