I have two concrete classes that inherit from an abstract class:
Public Class UpdateCommand
Inherits BaseCommand
Implements IUpdateCommand
Public Sub New(Root As DirectoryInfo)
MyBase.New(Root)
End Sub
End Class
Public Class CleanCommand
Inherits BaseCommand
Implements ICleanCommand
Public Sub New(Root As DirectoryInfo)
MyBase.New(Root)
End Sub
End Class
Public MustInherit Class BaseCommand
Implements IBaseCommand
Public Sub New(Root As DirectoryInfo)
Me.Root = Root
End Sub
Protected ReadOnly Property Root As DirectoryInfo Implements IBaseCommand.Root
End Class
...and then I have this class next to them:
Public Class Downloader
Implements IDownloader
Public Sub New(Command As IBaseCommand)
Me.Root = Command.Root
End Sub
Private ReadOnly Root As DirectoryInfo Implements IDownloader.Root
End Class
The interfaces are constructed similarly:
Public Interface IUpdateCommand
Inherits IBaseCommand
End Interface
Public Interface ICleanCommand
Inherits IBaseCommand
End Interface
Public Interface IBaseCommand
ReadOnly Property Root As DirectoryInfo
End Interface
Public Interface IDownloader
ReadOnly Property Root As DirectoryInfo
End Interface
My registration goes like so:
oRoot = New DirectoryInfo(Options.Root)
oParameters = New List(Of Parameter) From {
New NamedParameter(NameOf(Options.Root), oRoot)
}
oBuilder = New ContainerBuilder
oBuilder.RegisterType(Of UpdateCommand).As(Of IUpdateCommand).WithParameters(oParameters)
oBuilder.RegisterType(Of CleanCommand).As(Of ICleanCommand).WithParameters(oParameters)
oBuilder.RegisterType(Of Downloader).As(Of IDownloader)()
...and here's how I'm resolving them:
Dim oUpdateCommand As IUpdateCommand
Dim oCleanCommand As ICleanCommand
Using oScope As ILifetimeScope = Container.BeginLifetimeScope
oUpdateCommand = oScope.Resolve(Of IUpdateCommand)
oCleanCommand = oScope.Resolve(Of ICleanCommand)
oDownloader = oScope.Resolve(Of IDownloader)
End Using
Here's the error I'm getting when I try to resolve oDownloader
:
None of the constructors found on type 'VsLayout.Downloader' can be invoked with the available services and parameters: Cannot resolve parameter 'VsLayout.IBaseCommand Command' of constructor 'Void .ctor(VsLayout.IBaseCommand)'.
See https://autofac.rtfd.io/help/no-constructors-bindable for more info.
The documentation doesn't address this scenario, unfortunately.
I tried altering my registration slightly, specifying IBaseCommand
instead of IUpdateCommand
/ICleanCommand
:
oBuilder = New ContainerBuilder
oBuilder.RegisterType(Of UpdateCommand).As(Of IBaseCommand).WithParameters(oParameters)
oBuilder.RegisterType(Of CleanCommand).As(Of IBaseCommand).WithParameters(oParameters)
oBuilder.RegisterType(Of Downloader).As(Of IDownloader)()
...but that results in a different error when resolving:
Autofac.Core.Registration.ComponentNotRegisteredException HResult=0x80131500 Message=The requested service 'VsLayout.IUpdateCommand' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
See https://autofac.rtfd.io/help/service-not-registered for more info.
Again, the docs don't cover this specific scenario.
I then tried shifting the resolution a bit:
Dim oUpdateCommand As IUpdateCommand
Dim oCleanCommand As ICleanCommand
Dim oDownloader As IDownloader
Using oScope As ILifetimeScope = Container.BeginLifetimeScope
oUpdateCommand = oScope.Resolve(Of IBaseCommand)
oCleanCommand = oScope.Resolve(Of IBaseCommand)
oDownloader = oScope.Resolve(Of IDownloader)
End Using
That doesn't work, because an IBaseCommand
object can't be cast to an IUpdateCommand
. Sure, I could declare oUpdateCommand
as an IBaseCommand
, and that'd run, but IUpdateCommand
is eventually going to have unique properties on it that I'm going to need.
In the end, the Downloader
class must take an abstract as its parameter, because both UpdateCommand
and CleanCommand
are going to use it.
So are we stuck? How do we use Autofac with inheritance like this?
I've found the solution for this, in a great tip in the comments for this answer by @nemesv.
The working code:
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports Autofac
Imports Autofac.Core
Imports CommandLine
Imports CommandLine.Text
Imports Intexx.Linq
Friend Module Program
Friend Sub Main(Args As String())
Dim oParameters As List(Of Parameter)
Dim oHelpText As HelpText
Dim oResult As ParserResult(Of Options)
Dim oParser As Parser
Dim oRoot As DirectoryInfo
Dim eYear As Years
oParser = New Parser(Sub(Settings)
End Sub)
oResult = oParser.ParseArguments(Of Options)(Args)
oResult.
WithParsed(Sub(Options)
If Enums(Of Years).Descriptions.Contains(Options.Year) Then
oRoot = New DirectoryInfo(Options.Root)
Enums(Of Years).TryGetValue(Options.Year, eYear, SearchBy.Description)
oParameters = New List(Of Parameter) From {
New NamedParameter(NameOf(Options.Root), oRoot),
New NamedParameter(NameOf(Options.Year), eYear),
New NamedParameter(NameOf(Options.Edition), Options.Edition)
}
With New ContainerBuilder
.RegisterType(Of UpdateCommand).As(Of IBaseCommand).
Named(Of IBaseCommand)(NameOf(IUpdateCommand)).
WithParameters(oParameters)
.RegisterType(Of CleanCommand).As(Of IBaseCommand).
Named(Of IBaseCommand)(NameOf(ICleanCommand)).
WithParameters(oParameters)
.RegisterType(Of Downloader).As(Of IDownloader)()
Run(.Build)
End With
Else
oHelpText = HelpText.AutoBuild(oResult,
Function(HelpText) HelpText,
Function(Example) Example,
maxDisplayWidth:=120)
Console.WriteLine(oHelpText)
Environment.Exit(-1)
End If
End Sub).
WithNotParsed(Sub(Errors)
End Sub)
End Sub
Friend Sub Run(Container As IContainer)
Dim oUpdateCommand As IUpdateCommand
Dim oCleanCommand As ICleanCommand
Dim oDownloader As IDownloader
Using oScope As ILifetimeScope = Container.BeginLifetimeScope
oUpdateCommand = oScope.ResolveNamed(Of IBaseCommand)(NameOf(IUpdateCommand))
oCleanCommand = oScope.ResolveNamed(Of IBaseCommand)(NameOf(ICleanCommand))
oDownloader = oScope.Resolve(Of IDownloader)
End Using
End Sub
End Module
This has been an adventure!