Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible in Autofac to resolve all services for a type, even if they were registered with a name or key?

In Autofac one can do the following to get all registered services:

IEnumerable<MyClass> all = Context.Resolve<IEnumerable<MyClass>>()

However, this does not include those which were registered as named services.

Looking at the Autofac source it seems this is because services are queried for resolution based on either a TypedService or a KeyedService.

Is there a way to resolve all services to an IEnumerable, irrespective of whether they were registered with a name or not?

like image 596
Alex Norcliffe Avatar asked Feb 10 '11 15:02

Alex Norcliffe


3 Answers

The best option here is to register the items using both the key and the regular 'typed' service:

builder.Register<CupOfT>()
    .As<IBeverage>()
    .Keyed<IBeverage>("someKey");

You can then just resolve IEnumerable<IBeverage> to get the result you're after, while resolving them by key (or name) is also supported.

If you're concerned about maintaining a particular default registration for IBeverage just use PreseveExistingDefaults() on the others (or make sure your intended default is registered last).

HTH!

Nick

like image 116
Nicholas Blumhardt Avatar answered Apr 28 '23 09:04

Nicholas Blumhardt


I've written a method which appears to work; I'd appreciate feedback if there's a built-in way to do this in Autofac. In the below example, the field _context is of type IComponentContext.

    public IEnumerable<T> ResolveAll<T>()
    {
        // We're going to find each service which was registered
        // with a key, and for those which match the type T we'll store the key
        // and later supplement the default output with individual resolve calls to those
        // keyed services
        var allKeys = new List<object>();
        foreach (var componentRegistration in _context.ComponentRegistry.Registrations)
        {
            // Get the services which match the KeyedService type
            var typedServices = componentRegistration.Services.Where(x => x is KeyedService).Cast<KeyedService>();
            // Add the key to our list so long as the registration is for the correct type T
            allKeys.AddRange(typedServices.Where(y => y.ServiceType == typeof (T)).Select(x => x.ServiceKey));
        }

        // Get the default resolution output which resolves all un-keyed services
        var allUnKeyedServices = new List<T>(_context.Resolve<IEnumerable<T>>());
        // Add the ones which were registered with a key
        allUnKeyedServices.AddRange(allKeys.Select(key => _context.ResolveKeyed<T>(key)));

        // Return the total resultset
        return allUnKeyedServices;
    }
like image 38
Alex Norcliffe Avatar answered Apr 28 '23 10:04

Alex Norcliffe


It would appear that you can combine the As<T>() methods and Named<T>() methods as shown below:

    [TestMethod]
    public void ResolveTests()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<ClassA1>().As<IClassA>().Named<IClassA>("1");
        builder.RegisterType<ClassA2>().As<IClassA>().Named<IClassA>("2");
        builder.RegisterType<ClassA3>().As<IClassA>().Named<IClassA>("3");
        var container = builder.Build();

        var allInstances = container.Resolve<IEnumerable<IClassA>>();
        allInstances.Count().Should().Be(3);

        container.ResolveNamed<IClassA>("1").Should().BeAssignableTo<ClassA1>();
        container.ResolveNamed<IClassA>("2").Should().BeAssignableTo<ClassA2>();
        container.ResolveNamed<IClassA>("3").Should().BeAssignableTo<ClassA3>();
    }
like image 33
Tom Davis Avatar answered Apr 28 '23 10:04

Tom Davis