And therefore, here we are with this post answering the above questions.
Autofac allows its users to register the types explicitly or implicitly. While "
As" is used for explicit registrations, "
AsImplementedInterfaces" and "
AsSelf" are used for implicit ones. Please refer to the
autofac docs for more details.
Consider a class
BackupAndLogService, which implements two interfaces, namely
BackupService and
LoggingService. Another class,
DataController, has a dependency on the
BackupService for its data backup operations.
Now, there are two ways to register this dependency. Firstly, register
BackupAndLogService explicitly as
BackupService. However, if there comes another class which has a dependency of
LoggingService, our code will fail. In order to meet the second requirement, we again need to register
BackupAndLogService. explicitly as
LoggingService.
Secondly, we can register
"BackupAndLogService" with the container using
AsImplementedInterfaces. As a result, the container automatically registers the implementation against all the interfaces it implements. Therefore, when we add a dependency of either of the interfaces implemented by
BackupAndLogService, the container will be able to resolve it.
The Bad Smell
The problem occurs when we have two classes implementing a common interface and they both are registered using
AsImplementedInterfaces. In such a case, the container will always provide the second implementation while we might be expecting the first one.
Assume, there is another class
WindowsLoggingAndGeneralService, which provides (implements)
LoggingService and
GeneralService to windows users of the application. And, we register the two implementation classes as shown in the code below.
- private static void RegisterTypes(ContainerBuilder builder)
- {
- builder.RegisterType < DataController > ();
- builder.RegisterType < BackupAndLogService > ().AsImplementedInterfaces();
- builder.RegisterType < WindowsLoggingAndGeneralService > ().AsImplementedInterfaces();
- }
At this point of time, if we add the dependency of
LoggingService in the constructor of
DataController, our application will have a different behavior for non-windows users. While resolving the
LoggingService, the second registration will always win. This is because the container always considers the latest registration made against a service while resolving it.
The image shows the result of dependency resolution based on the registrations we have in the above code sample. Such situations usually occur if we are not careful with our registrations. However, in this particular scenario, we can fix the problem by registering any one of the implementations explicitly. I would leave that for you to try and observe the changes.
Well, I do agree that this might not be the best example to describe this problem but I believe this will still encourage you to be more careful with your dependency registrations.
Thank you!