Autofac如何注册/解析构造函数含抽象类型参数的类型?
问题背景
现有如下类和接口结构:
命令类与抽象基类
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
Downloader类
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
接口定义
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
当前注册与解析代码
注册代码:
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)()
解析代码:
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
遇到的错误
解析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)'.
尝试将命令类注册为IBaseCommand后,又触发新错误:
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.
解决方案
方案一:注册命令类时同时关联父接口
Autofac默认不会自动将实现类注册为其接口的父接口,因此需要显式指定命令类同时注册为IBaseCommand和各自的专属接口。修改注册代码如下:
oBuilder.RegisterType(Of UpdateCommand) _ .As(Of IUpdateCommand) _ .As(Of IBaseCommand) _ .WithParameters(oParameters) oBuilder.RegisterType(Of CleanCommand) _ .As(Of ICleanCommand) _ .As(Of IBaseCommand) _ .WithParameters(oParameters)
注意:如果直接解析IBaseCommand,Autofac会返回最后注册的实现类(此处为CleanCommand)。若需要根据场景注入对应命令,可结合键控服务区分:
' 注册时标记键名 oBuilder.RegisterType(Of UpdateCommand) _ .Keyed(Of IBaseCommand)("update") _ .As(Of IUpdateCommand) _ .WithParameters(oParameters) oBuilder.RegisterType(Of CleanCommand) _ .Keyed(Of IBaseCommand)("clean") _ .As(Of ICleanCommand) _ .WithParameters(oParameters) ' 注册Downloader时指定注入对应键的服务 oBuilder.RegisterType(Of Downloader).As(Of IDownloader) _ .WithParameter(ResolvedParameter.ForKeyed(Of IBaseCommand)("update"))
方案二:解析Downloader时手动传入命令实例
若不需要全局注册IBaseCommand,可在解析Downloader时,将已获取的IUpdateCommand/ICleanCommand实例作为参数传入:
Using oScope As ILifetimeScope = Container.BeginLifetimeScope oUpdateCommand = oScope.Resolve(Of IUpdateCommand) ' 传入已解析的UpdateCommand作为IBaseCommand参数 oDownloader = oScope.Resolve(Of IDownloader)(New TypedParameter(GetType(IBaseCommand), oUpdateCommand)) End Using
方案三:使用工厂模式动态注入
如果需要根据不同场景自动匹配对应的命令与Downloader,可定义工厂接口并注册:
Public Interface IDownloaderFactory Function Create(command As IBaseCommand) As IDownloader End Interface ' 实现工厂类 Public Class DownloaderFactory Implements IDownloaderFactory Public Function Create(command As IBaseCommand) As IDownloader Implements IDownloaderFactory.Create Return New Downloader(command) End Function End Class
注册工厂:
oBuilder.RegisterType(Of DownloaderFactory).As(Of IDownloaderFactory)()
解析时通过工厂创建Downloader:
Using oScope As ILifetimeScope = Container.BeginLifetimeScope oUpdateCommand = oScope.Resolve(Of IUpdateCommand) Dim factory = oScope.Resolve(Of IDownloaderFactory) oDownloader = factory.Create(oUpdateCommand) End Using
内容的提问来源于stack exchange,提问作者InteXX

