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.
- The Ordinary Case
- Here Come The Primitives
- Pain Points
- Service Locator Is The Wrong Solution
- Overcoming The Primitive Obsession
- Value Objects In Action
implicit
andexplicit
To The Rescue- Putting It All Together
- Conclusion
The Ordinary Case
Say you have a class Foo
depending on Bar
:
1 | class Foo |
Registering both of them
with Autofac is just simple as writing:
1 | var builder = new ContainerBuilder(); |
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.
Here Come The Primitives
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
:
1 | class Foo |
You are offered a bunch of native Autofac facilities for registering this class.
Either you can use a lambda:
1 | builder.Register(c => |
or you can continue using the ordinary RegisterType<>()
, enhanced with the withParameter()
facility:
1 | builder.RegisterType<Foo>() |
Pain Points
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:
1 | builder.Register(c => |
or
1 | builder.RegisterType<Foo>() |
Not only is it verbose and repetitive, but the resulting code is also affected by a couple of problems:
- both the URL and the connection string are represented with the same class (a string), giving no chances to the compiler to know which is which; consequently, it is very possible to switch their values without giving the receiving class the opportunity to detect the issue but at runtime.
Would you spot the problem in the following code?
1 | class Foo |
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.
The following code compiles, but it will throw an exception at runtime the moment you will try to resolveFoo
:
1 | class 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.
Service Locator Is The Wrong Solution
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:
1 | var connectionString = ConfigurationManager.AppSetting["myConnection"]; |
This may suggest you a trick you could be tempted to use: to directly inject ConfigurationManager.AppSetting
into your classes:
1 | class Foo |
Voilà. No more primitives!
You could even be inclined to define a custom service for collecting all of your configuration parameters:
1 | class MyConfigs |
so that you can easily inject it, as a glorified configuration parameters repository:
1 | class Foo |
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.
Overcoming The Primitive Obsession
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:
1 | class Foo |
you would actually have 3 distinct parameters, each well defined with its specific class, just like all the other domain ideas:
1 | class Foo |
So, the basic trick for dealing with primitives in Autofac is: don’t use primitives. Just represent your configuration parameters with non-primitive types.
Value Objects In Action
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
:
1 | class MaxUsers : 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.
1 | class MaxUsers |
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:
1 | class BarServiceAuthParameters |
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:
1 | connectionString.Value |
instead of
1 | connectionString |
since it’s not a string anymore.
It’s also more difficult to assign it a value. Instead of
1 | var connectionString = "foobar"; |
you need
1 | var connectionString = new ConnectionString("foobar"); |
That’s bad.
implicit
and explicit
Cast Operators To The Rescue
Let’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
1 | public class ConnectionString |
you will get the benefit to consuming and creating it as a primitive, as in the following example:
1 |
|
Putting It All Together
Once you define all your configuration parameters as Value Objects (with the implicit
trick), you can use them like the following:
1 | class Foo |
Luca’s GitHub hosta a sample project leveraging this technique.
Conclusion
- Don’t use primitives. Use Value Objects;
- Avoid using the Service Locator pattern: it’s bad;
- Define Value Objects wrapping your configurations, using the
implicit
cast operator to make them behave as a primitive; - Register them with
RegisterInstance()
.
You will get:
- easy and straightforward Autofac register statements, without using lambdas or
withParameter()
; - static compile time type checking on injected parameters (no more dependencies neither on parameters order nor on parameters names);
- easier refactoring, you can easily rename parameters or change the order of them, without breaking anything;
- all the advantages of using Value Objects, including the possibility to validate the primitive value;
- the above solution can used also for collection of primitives or classes, such as
List<string>
.
(Post repository on Luca Trazzi’s Github)