Registering components in Autofac is straightforward, as long as no primitive dependencies (such as connection strings, URLs and configuration parameters in general) are involved.
This post describes the strategy for dealing with primitive dependencies.
implicit
and explicit
To The RescueSay you have a class Foo
depending on Bar
:
Registering both of them with Autofac is just simple as writing:
This is enough for Autofac: since it knows how to create instances of Bar
(it’s just a matter of invoking its default constructor), it also knows how to build instances of Foo
.
This works with no additional configurations even if dependencies are reversed (e.g Bar
depends on Foo
) or if relationships are implicit, for example when Foo
depends on Func<Bar>
, or on List<Bar>
and the like: Autofac is smart enough to build an instance of the right class and inject it into the right component.
Troubles come when one of the dependencies is a primitive. Suppose that Foo
also depends on a connection string, which you decided to represent as a string
:
You are offered a bunch of native Autofac facilities for registering this class.
Either you can use a lambda:
or you can continue using the ordinary RegisterType<>()
, enhanced with the withParameter()
facility:
This is tollerable as long as there are just a very little number of primitive dependencies.
It starts stinking when it ends up with code like the following:
or
Not only is it verbose and repetitive, but the resulting code is also affected by a couple of problems:
The compiler wouldn’t, and that’s a pity;
withParameter
references parameters by name, as a string, so simple refactoring tasks such as renaming variables become very fragile.Foo
:Yes, it’s just a matter of a capitalized S
. Hard to spot, isn’t it?
The bad habit of using primitive types to represent domain ideas is a smell called Primitive Obsession.
Let’s see how to avoid it without endng up with ugly Autofac registration statements.
Why do you need configuration parameters, in the first place? Of course because you want the freedom to change them at runtime, presumably by using a configuration file:
This may suggest you a trick you could be tempted to use: to directly inject ConfigurationManager.AppSetting
into your classes:
Voilà. No more primitives!
You could even be inclined to define a custom service for collecting all of your configuration parameters:
so that you can easily inject it, as a glorified configuration parameters repository:
This seems to solve most of the problems related to Primitive Obsession, right?
Well, yes, at least it solves the ugly Autofac registration statements issue. In fact, it introduces some additional problems, possibly worse than the ones it was supposed to solve.
The problem is: that’s a Service Locator.
I strongly suggest you to read the seminal Mark Seemann’s post Service Locator Is An Anti-Pattern which collects a lot of strong arguments on why you should avoid using the Service Locator pattern. Basically, the root of Service Locator’s evil is that it hides class dependencies, causing run-time errros and maintenance additional burden.
Service Locator pattern is mostly related to services, while here you are dealing with values without behaviour, simple strings and integers; yet Mark Seemann’s argument apply: injecting a configuration-parameters locator is an anti-pattern anyway.
Just don’t do it.
Let me try to convince you that there is something deeply wrong with injecting a primitive.
Say you have 2 configuration parameters: maxUsers
and numerOfItemsPerPage
. They can be defined in 2 completely different contexts, represent 2 completely different ideas, and have nothing to share.
Yet you can represent both of them with the same type, int
.
That’s the root error: when you use the very same class for 2 completely different purposes, it’s just like collapsing CustomerController
and NHibernateSession
to a single class: by doing so, you would give no chance neither to Autofac nor to the compiler itself to distinguish the former from the latter.
It’s easy to see why representing the customer controller and the NHibernate session with 2 dedicated classes is valuable: it isn’t hard to see that the idea can be profitably applied to maxUsers
and numerOfItemsPerPage
well, and in general to any primitive configuration parameter. It would give you the opportunity the rely on the compiler: instead of having a constructor which takes 3 indistinguishable integers:
you would actually have 3 distinct parameters, each well defined with its specific class, just like all the other domain ideas:
So, the basic trick for dealing with primitives in Autofac is: don’t use primitives. Just represent your configuration parameters with non-primitive types.
Ok. Sounds simple.
So, instead of declaring maxUsers
and numberOfItemsPerPage
as int
, all you have to do is to define 2 separate non-primitive types inheriting from int
:
Unfortunately, this is not allowed in C#, since primitive types are sealed.
You definitely have to resort on a workaround: use a DTO as a wrapper of the primitive value.
Think about it: isn’t it exactly what you are already used to do, when dealing with compound configuration parameters? For example, chances are you had the need to inject into an instance the url, the username and the password for accessing a web service, and you decided to group them into a single DTO:
Wasn’t it easy to inject, since it’s an ordinary class?
The trick is: use the same strategy also when dealing with single primitive values.
In the the very short post Primitive Obsession J.B. Rainsberger claims those kind of Value Object
[…] become “attractive code”, meaning literally a class/module that attracts behavior towards it as new methods/functions. For example, instead of scattering all the parsing/formatting behavior for dates stored as text, introduce a DateFormat class which attracts that behavior as methods called parse() and format(), almost like magic.
So, it’s likely that just by introducing a class for representing an URL or a connection string you will end up enhancing it with some formatting or validation logic, which you could not do with a plain, primitive string
.
Unfortunately, this solution has got it’s drawbacks too. Now it’s just more difficult to consume the ConnectionString
. You need to write:
instead of
since it’s not a string anymore.
It’s also more difficult to assign it a value. Instead of
you need
That’s bad.
implicit
and explicit
Cast Operators To The RescueLet’s see what you can do in order to make that Value Object resemble a primitive.
It would be nice if it were possible to implicitly cast it from and to string
.
Actually, that’s not too hard to achieve. There is a technique Jimmy Bogard brillantly exposed in his post Dealing with primitive obsession that makes a smart use of the cast operators implicit
and explicit
and allows to make you consume and create your Value Objects as they are primitives.
Go and read the post. You will learn that by defining your Value Object as
you will get the benefit to consuming and creating it as a primitive, as in the following example:
Once you define all your configuration parameters as Value Objects (with the implicit
trick), you can use them like the following:
Luca’s GitHub hosta a sample project leveraging this technique.
implicit
cast operator to make them behave as a primitive;RegisterInstance()
.You will get:
withParameter()
;List<string>
.(Post repository on Luca Trazzi’s Github)