Question
How do I construct an AutoFac ContainerBuilder such that my sub-dependencies are correctly resolved (assuming more than one concrete implementation of an interface)? Default registration/resolve will not work because I have more than one concrete implementation of a sub-dependency, and the sub-dependency resolution is dependent on the resolution of the primary object. In all cases I wish to use constructor injection.
Scenario
For example, lets say I need to print a receipt, so I create an interface called IReceipt, with one method, called PrintReceipt().
But I need to print 3 kinds of receipts
- Standard Receipt
- Gift Receipt
- Email Receipt
All receipt types have different formats, and the email receipt is not printed at all, rather it is emailed. So I wish my IReceipt had the ability to depend on a formatter, and a processor. Say I invent IProcessor, with the method Process(), and it can either be a printer processor, or an email processor (the printer processor has the responsibility of printer communications, and the email processor has the responsibility of communicating with the SMTP server). Also, I create an IFormatter, and it can feed input to the processor object, formatted as a standard receipt, or a gift receipt, or even HTML for the email receipt. So the constructor on any concrete implementation of an IReciept now requires two dependencies - an IFormatter, and an IProcessor.
Now, at the root composition I need to decide, am I resolving an IReceipt that is intended for a standard receipt, a Gift receipt, or an email receipt. I would like to call the container Resolve() method passing the necessary parameters so it will resolve the correct IReceipt. Furthermore, I would like the registration ContainerBuilder to know that if I attempt to resolve the IReceipt Standard Receipt concrete implementation that it needs to resolve sub-depencies with the correct Standard Receipt IFormatter, and correct Standard Receipt IProcessor. The same would hold true for the gift receipt scenario and the email receipt scenario.
Recap
So - in all this - my question is - how do I construct the ContainerBuilder so sub-dependencies are defined at design time, and that a single call to Resolve() will correctly identify the required concrete implementations? I am not seeking a solution to talk to a printer, or post HTML. This question is strictly for the Autofac registration and resolution methodology. At a different client, I have used this exact tactic using CastleWindsor but my current client is using Autofac.
OK, so I figured out a way to perform the sub-dependency chaining. Again, the desire is to call resolve once per root composition object and have all sub-dependencies satisfied the entire chain down. Not looking to get into a religious debate about best practices - whether or not factory methods, service locators, etc. should be implemented. I intentionally left out IoC scoping as it is not the subject of my original question.
This contrived example does not implement email or printer functionality, but it is stubbed out now. The whole point is to show how to pre-define the entire dependency chain. Now automated unit tests will be easier to implement.
Code
Main Program
Interfaces
IPrinter
IReceipt
IProcessor
IFormatter
Concrete Implementations
Will show the leaf dependencies first...
Printer
Printer Processor
Email Processor
Standard Receipt Formatter
Gift Receipt Formatter
Email Receipt Formatter
Receipt
Conclusion
Most of the noise associated with objects that need to be created is bundled into one location. In practice I would likely move the registration to its own chunk of code (Perhaps a static class). Keeping the noise out of the functional classes yields nice clean code with a focus on functional intent. All the wire-up is early in the process, and now out of the way.
Looking at the registration in particular, my contrived example has 4 layers of dependencies..
By using the Autofac.Core.ResolvedParameter we can reference other registered objects (by name). This will keep the registration long, but flat. Any hierarchy is expressed (shallowly - if that's a word) as parent-child only, and very re-usable. Resolve is only called on the root composition objects - the 3 receipt engines (Standard, Gift, and Email). For each root composition object the entire chain of dependencies is now resolvable.