There are times when you may want to register multiple components that all expose the same service but you want to pick which component is used in different instances.

For this question, let’s imagine a simple order processing system. In this system, we have…

A shipping processor that orchestrates the physical mailing of the order contents.

A notification processor that sends an alert to a user when their order status changes.

In this simple system, the shipping processor might need to take different “plugins” to allow order delivery by different means - postal service, UPS, FedEx, and so on. The notification processor might also need different “plugins” to allow notifications by different means, like email or SMS text.

Your initial class design might look like this:

// This interface lets you send some content// to a specified destination.publicinterfaceISender{voidSend(Destinationdest,Contentcontent);}// We can implement the interface for different// "sending strategies":publicclassPostalServiceSender:ISender{...}publicclassEmailNotifier:ISender{...}// The shipping processor sends the physical order// to a customer given a shipping strategy (postal service,// UPS, FedEx, etc.).publicclassShippingProcessor{publicShippingProcessor(ISendershippingStrategy){...}}// The customer notifier sends the customer an alert when their// order status changes using the channel specified by the notification// strategy (email, SMS, etc.).publicclassCustomerNotifier{publicCustomerNotifier(ISendernotificationStrategy){...}}

When you register things in Autofac, you might have registrations that look like this:

When you run into a situation where you have a bunch of components that implement identical services but they can’t be treated identically, this is generally an interface design problem.

From an object oriented development perspective, you’d want your objects to adhere to the Liskov substitution principle and this sort of breaks that.

Think about it from another angle: the standard “animal” example in object orientation. Say you have some animal objects and you are creating a special class that represents a bird cage that can hold small birds:

OK, there are our animals. Obviously we can’t treat them all equally, so if we made a bird cage class, we probably wouldn’t do it like this:

publicclassBirdCage{publicBirdCage(Animalanimal){if(!(animalisBird)||animal.Size!=AnimalSize.Small){// We only support small birds.thrownewNotSupportedException();}}}

Designing your bird cage to take just any animal doesn’t make sense. You’d at least want to make it take only birds:

publicclassBirdCage{publicBirdCage(Birdbird){if(bird.Size!=AnimalSize.Small){// We know it's a bird, but it needs to be a small bird.thrownewNotSupportedException();}}}

But if we change the class design just a little bit, we can make it even easier and force only the right kind of birds to even be allowed to be used:

// We still keep the base Bird class...publicabstractclassBird:Animal{publicoverridestringMakeNoise(){return"Chirp!";}}// But we also add a "PetBird" class - for birds that// are small and kept as pets.publicabstractclassPetBird:Bird{// We "seal" the override to ensure all pet birds are small.publicsealedoverrideAnimalSize{get{returnAnimalSize.Small;}}}// A parakeet is a pet bird, so we change the base class.publicclassParakeet:PetBird{}// Bald eagles aren't generally pets, so we don't change the base class.publicclassBaldEagle:Bird{publicoverridestringMakeNoise(){return"Screech!";}publicoverrideAnimalSize{get{returnAnimalSize.Large;}}}

Now it’s easy to design our bird cage to only support small pet birds. We just use the correct base class in the constructor:

publicclassBirdCage{publicBirdCage(PetBirdbird){}}

Obviously this is a fairly contrived example with flaws if you dive too far into the analogy, but the principle holds - redesigning the interfaces helps us ensure the bird cage only gets what it expects and nothing else.

Bringing this back to the ordering system, it might seem like every delivery mechanism is just “sending something” but the truth is, they send very different types of things. Maybe there’s a base interface that is for general “sending of things,” but you probably need an intermediate level to differentiate between the types of things being sent:

// We can keep the ISender interface if we want...publicinterfaceISender{voidSend(Destinationdest,Contentcontent);}// But we'll introduce intermediate interfaces, even// if they're just "markers," so we can differentiate between// the sort of sending the strategies can perform.publicinterfaceIOrderSender:ISender{}publicinterfaceINotificationSender:ISender{}// We change the strategies so they implement the appropriate// interfaces based on what they are allowed to send.publicclassPostalServiceSender:IOrderSender{...}publicclassEmailNotifier:INotificationSender{...}// Finally, we update the classes consuming the sending// strategies so they only allow the right kind of strategy// to be used.publicclassShippingProcessor{publicShippingProcessor(IOrderSendershippingStrategy){...}}publicclassCustomerNotifier{publicCustomerNotifier(INotificationSendernotificationStrategy){...}}

By doing some interface redesign, you don’t have to “choose a dependency by context” - you use the types to differentiate and let auto-wireup magic happen during resolution.

If you have the ability to affect change on your solution, this is the recommended option.

One of the things Autofac lets you do when you register components is to register lambda expressions rather than just types. You can manually associate the appropriate type with the consuming component in that way:

If you want to keep the senders being resolved from Autofac, you can expose them both as their interface types and as themselves, then resolve them in the lambda:

varbuilder=newContainerBuilder();// Add the "AsSelf" clause to expose these components// both as the ISender interface and as their natural type.builder.RegisterType<PostalServiceSender>().As<ISender>().AsSelf();builder.RegisterType<EmailNotifier>().As<ISender>().AsSelf();// Lambda registrations resolve based on the specific type, not the// ISender interface.builder.Register(ctx=>newShippingProcessor(ctx.Resolve<PostalServiceSender>()));builder.Register(ctx=>newCustomerNotifier(ctx.Resolve<EmailNotifier>()));varcontainer=builder.Build();

Perhaps you are able to change your registrations but you are also using modules to register lots of different components and can’t really tie things together by type. A simple way to get around this is to use keyed services.

In this case, Autofac lets you assign a “key” or “name” to a service registration and resolve based on that key from another registration. In the module where you register your senders, you would associate the appropriate key with each sender; in the module where you register you processors, you’d apply parameters to the registrations to get the appropriate keyed service dependency.

Now when the processors are resolved, they’ll search for the keyed service registrations and you’ll get the right one injected.

You can have more than one service with the same key so this will work if you have a situation where your sender takes in an IEnumerable<ISender> as well via implicitly supported relationships. Just set the parameter to ctx.ResolveKeyed<IEnumerable<ISender>>("order") with the appropriate key in the processor registration; and register each sender with the appropriate key.

If you have the ability to change the registrations and you’re not locked into doing assembly scanning for all your registrations, this is the recommended option.

If you need something more flexible than keyed services or if you don’t have the ability to directly affect registrations, you may want to consider using the registration metadata facility to tie the right services together.

If you can’t change the registrations of the sender components, but you’re allowed to change the object definitions, you can add metadata to components using the “attribute metadata” mechanism. First you’d create your custom metadata attribute: