It seems like a bad idea to use service keys (or 'named services') when composing a container.
Using named services requires us to either annotate our constructor parameters with matching keys (thus coupling with a container), or to perform additional wiring for each of our services (thus loosing a lot of automation from the container).
For example, I currently have the following interface which is implemented by the following classes:
IListSerializer
CheckboxListSerializer
TreeViewListSerializer
I also have countless classes which depend on either one or both of these classes. However, AFAIK I should be referencing the IListSerializer
as my dependency rather than the implementations. This means I have to use keys/names to differentiate between them, which is where it starts to get ugly.
I can see my options as being one of the following:
Any suggestions?
Whether you will have the need to use some other component in place of the current one, is need based. Use of IOC is to prepare the code for such a change, if it arises IMO.
You can waste days evaluating IOC containers. The top ones are quite similar. There is not much in this, but the best ones are StructureMap and AutoFac. At SSW we use Autofac on most projects.
Spring IoC Container is the core of Spring Framework. It creates the objects, configures and assembles their dependencies, manages their entire life cycle. The Container uses Dependency Injection(DI) to manage the components that make up the application.
In general, the Liskov Substitution Principle is a very useful guide when designing components and services for IoC. If two implementations of a service can't be used interchangeably at runtime, then the service is too general to be meaningful. In this scenario I'd look at using something along the lines of IListSerializer<T>
if that is an option for you.
However, if you want to use named services, this is easy and unintrusive to set up with Autofac.
First, register each serializer with its name:
builder.RegisterType<CheckBoxListSerializer>()
.Named<IListSerializer>("checkBoxSerializer");
builder.RegisterType<TreeViewListSerializer>()
.Named<IListSerializer>("treeViewSerializer");
Then, add a globally-available parameter that uses the constructor parameter name to choose the right implementation. We can do this with a module:
class NamedParameterResolutionModule<TService> : Module
{
Parameter _attachedParameter = new ResolvedParameter(
(pi, c) => pi.ParameterType == typeof(TService),
(pi, c) => c.ResolveNamed<TService>(pi.Name));
protected override void AttachToComponentRegistration(
IComponentRegistry registry,
IComponentRegistration registration)
{
registration.Preparing += (s, e) => {
e.Parameters = new[] { _attachedParameter }.Contact(e.Parameters);
};
}
}
Register the module like so:
builder.RegisterModule<NamedParameterResolutionModule<IListSerializer>>();
Components will then get a serializer depending on the constructor parameter name:
class SomeComponent : ...
{
public SomeComponent(IListSerializer checkBoxSerializer) { ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With