How can I use Autofac to resolve interface implementations only if they are no longer "specialized"?

Summary

I would like to know if it is possible to get Autofac to return an enumeration of all registered types that implement the interface, provided that they do not also implement the interface that comes from the requested one (ie was not "more specialized" than was required) ...

Skip to the bottom of the sample code that demonstrates this.

Background

In the program I am writing, I have a "controller" class that (until recently) accepted collections of 4 different types of collaborators, allowed and injected into it using Autofac during application startup:

public DeviceComponentController(
    IEnumerable<Lazy<IClientRequestProcessor<T>>> clientRequestProcessors,
    IEnumerable<Lazy<IClientResponseGenerator<T>>> clientResponseGenerators,
    IEnumerable<Lazy<IComponentDeviceMessageDecoder<T>>> deviceMessageDecoders,
    IEnumerable<Lazy<IComponentDeviceMessageEncoder<T>>> deviceMessageEncoders)
{
    // ...
}

      

However, I found that in some cases I needed employees to execute certain logic that didn't really fit into any of these roles (i.e. wasn't directly related to handling client requests, generating responses, etc.) ). I could "straddle" this logic on a class that already handled one of these things, but that would violate the single responsibility principle.

Instead, I created a new interface IDeviceComponentControllerCollaborator

(quite verbose, I know!) From which these other interfaces extended:

interface IDeviceComponentControllerCollaborator
{
    // ...
}

interface IClientRequestProcessor : IDeviceComponentControllerCollaborator
{
    // ...
}

interface IClientResponseGenerator : IDeviceComponentControllerCollaborator
{
    // ...
}

// Etc.

      

I modified the controller constructor to accommodate additional collaborators who did not fit the clarity of any of the existing roles, but could interact with other collaborators:

public DeviceComponentController(
    IEnumerable<Lazy<IClientRequestProcessor<T>>> clientRequestProcessors,
    IEnumerable<Lazy<IClientResponseGenerator<T>>> clientResponseGenerators,
    IEnumerable<Lazy<IComponentDeviceMessageDecoder<T>>> deviceMessageDecoders,
    IEnumerable<Lazy<IComponentDeviceMessageEncoder<T>>> deviceMessageEncoders,
    IEnumerable<Lazy<IDeviceComponentControllerCollaborator<T>>> genericCollaborators)
{
    // ...
}

      

However, I'm not entirely sure what is the best way to make Autofac allow the parameter genericCollaborators

when requested DeviceComponentController

. I would like to list every registered IDeviceComponentControllerCollaborator

one that is not a client request processor, client response generator, etc.

SSCCE

I have reduced the problem to the example code below. Any solution for this code example should hopefully solve the problem I am facing.

void Main()
{
    var builder = new ContainerBuilder();

    // These types _must_ be registered with all implemented interfaces.
    // In my real program I use an extension method to register these types,
    // which for simplicity uses `.AsImplementedInterfaces()`.
    //
    // However, the extension method in my program returns the registration
    // builder, so I can extend the registration on a case-by-case basis if I want
    // (e.g. adding metadata).
    builder.RegisterType<Foo>().AsImplementedInterfaces();
    builder.RegisterType<Bar>().AsImplementedInterfaces();
    builder.RegisterType<Baz>().AsImplementedInterfaces();
    builder.RegisterType<SomeGenericCollaborator>().AsImplementedInterfaces();
    builder.RegisterType<SomeOtherGenericCollaborator>().AsImplementedInterfaces();
    builder.RegisterType<YetAnotherGenericCallaborator>().AsImplementedInterfaces();

    var container = builder.Build();

    // Should give me Foo.
    var collaboratorsA = container.Resolve<IEnumerable<ISpecialisedCollaboratorA>>();

    // Should give me Bar and Baz.
    var collaboratorsB = container.Resolve<IEnumerable<ISpecialisedCollaboratorB>>();

    // Problem: how do I resolve just the "generic" collaborators?
    // I.e.
    //   - SomeGenericCollaborator
    //   - SomeOtherGenericCollaborator
    //   - YetAnotherGenericCallaborator
    //
    // var genericCollaborators = container.Resolve<...>(); ???
    //
    // This does not work (returns all 6 collaborators):
    // var genericCollaborators = container.Resolve<IEnumerable<ICollaborator>>();
}

// Interfaces
interface ICollaborator
{
}

interface ISpecialisedCollaboratorA : ICollaborator
{
}

interface ISpecialisedCollaboratorB : ICollaborator
{
}

// Implementation
class Foo : ISpecialisedCollaboratorA
{
}

class Bar : ISpecialisedCollaboratorB
{
}

class Baz : ISpecialisedCollaboratorB
{
}

class SomeGenericCollaborator : ICollaborator
{
}

class SomeOtherGenericCollaborator : ICollaborator
{
}

class YetAnotherGenericCallaborator : ICollaborator
{
}

      

Will this be something I will need to solve using named / keyed services, or is there an alternative?

+3


source to share





All Articles