我有这些课程:
Public Class ProcessRunner
Implements IProcessRunner
Public Sub New(Command As ICommand)
End Sub
End Class
Public Class Command
Implements ICommand
Public Sub New(BootStrapper As IBootStrapper)
End Sub
End Class
Public Class BootStrapper
Implements IBootStrapper
Public Sub New(Releases As IReleases)
End Sub
End Class
Public Class Releases
Implements IReleases
Public Sub New(Repository As IRepository)
End Sub
End Class
Public Class Repository
Implements IRepository
Public Sub New(Context As DbContext)
End Sub
End Class
Public Class MsSqlContext
Inherits DbContext
End Class
Public Class SqliteContext
Inherits DbContext
End Class
我正在注册第一个这样的:
Builder.RegisterType(Of ProcessRunner).As(Of IProcessRunner)
Builder.RegisterType(Of BootStrapper).As(Of IBootStrapper)
Builder.RegisterType(Of Releases).As(Of IReleases)
Builder.RegisterType(Of Command).As(Of ICommand)
...然后我注册
Respository
和 MsSqlContext
/SqliteContext
像这样:
Dim oSqliteParameter As ResolvedParameter
Dim oSqliteAccessor As Func(Of ParameterInfo, IComponentContext, Object)
Dim oMsSqlParameter As ResolvedParameter
Dim oMsSqlAccessor As Func(Of ParameterInfo, IComponentContext, Object)
Dim oSqlPredicate As Func(Of ParameterInfo, IComponentContext, Boolean)
oSqliteAccessor = Function(Info, Context) Context.ResolveKeyed(Of DbContext)(NameOf(SqliteContext))
oMsSqlAccessor = Function(Info, Context) Context.ResolveKeyed(Of DbContext)(NameOf(MsSqlContext))
oSqlPredicate = Function(Info, Context) Info.ParameterType = GetType(DbContext)
oSqliteParameter = New ResolvedParameter(oSqlPredicate, oSqliteAccessor)
oMsSqlParameter = New ResolvedParameter(oSqlPredicate, oMsSqlAccessor)
With Builder
.RegisterType(Of Repository(Of Dto.Release, Model.Release)).
WithParameter(oSqliteParameter).
Keyed(Of IRepository(Of Dto.Release, Model.Release))(NameOf(SqliteContext)).
As(Of IRepository(Of Dto.Release, Model.Release))()
.RegisterType(Of Repository(Of Dto.Release, Model.Release)).
WithParameter(oMsSqlParameter).
Keyed(Of IRepository(Of Dto.Release, Model.Release))(NameOf(MsSqlContext)).
As(Of IRepository(Of Dto.Release, Model.Release))()
.RegisterType(Of SqliteContext).
InstancePerLifetimeScope.
Keyed(Of DbContext)(NameOf(SqliteContext)).
As(Of DbContext)()
.RegisterType(Of MsSqlContext).
InstancePerLifetimeScope.
Keyed(Of DbContext)(NameOf(MsSqlContext)).
As(Of DbContext)()
End With
问题是,当我解决
ProcessRunner
时,MsSqlContext
总是在内部发送到Repository
,大概是因为它按字母顺序排在第一位。我的测试失败了,因为我最终针对生产数据库进行了测试。
我找到了这个答案,但是好像不适用。对于初学者来说,没有像这里那样的深度依赖链。我真的必须一直注册所有内容的两个实例吗?有没有一种更简洁的方法可以通过层次结构向下传递上下文名称?
我咨询了 ChatGPT(当然!)但它返回了一些损坏的非编译代码,模糊地类似于
IRegistrationSource
的实现。我查看了docs for that,但我觉得不太合适。 (如果我对那个评估有误,请纠正我。)
我怎样才能最好地注册和解决所有问题,以控制在解决
Respository
时将哪个Releases
发送到ProcessRunner
构造函数,因为我不知道在解决时间之前使用哪个Respository
(测试与生产)?
最终可能会以某种方式走
NameOf(SqliteContext)
或 NameOf(MsSqlContext)
在堆栈中,但我不确定该怎么做。
(注意:用 VB.NET 或 C# 回答都可以。)
--编辑--
可以肯定的是,这是一个拼凑,但我至少能够使用一系列复杂的扩展方法让它工作:
Friend Module Extensions
<Extension>
Friend Function ProcessRunner(Of TContext As DbContext)(Instance As ILifetimeScope) As IProcessRunner
Return Instance.Resolve(Of IProcessRunner)(Instance.CommandParameter(Of TContext))
End Function
<Extension>
Friend Function Command(Of TContext As DbContext)(Instance As ILifetimeScope) As ICommand
Return Instance.Resolve(Of ICommand)(Instance.BootStrapperParameter(Of TContext))
End Function
<Extension>
Friend Function BootStrapper(Of TContext As DbContext)(Instance As ILifetimeScope) As IBootStrapper
Return Instance.Resolve(Of IBootStrapper)(Instance.ReleasesParameter(Of TContext))
End Function
<Extension>
Friend Function Releases(Of TContext As DbContext)(Instance As ILifetimeScope) As IReleases
Return Instance.Resolve(Of IReleases)(Instance.RepositoryParameter(Of TContext))
End Function
<Extension>
Friend Function Repository(Of TContext As DbContext)(Instance As ILifetimeScope) As IRepository
Return Instance.ResolveKeyed(Of IRepository)(GetType(TContext).Name)
End Function
<Extension>
Private Function RepositoryParameter(Of TContext As DbContext)(Instance As ILifetimeScope) As TypedParameter
Return TypedParameter.From(Instance.Repository(Of TContext))
End Function
<Extension>
Private Function ReleasesParameter(Of TContext As DbContext)(Instance As ILifetimeScope) As TypedParameter
Return TypedParameter.From(Instance.Releases(Of TContext))
End Function
<Extension>
Private Function BootStrapperParameter(Of TContext As DbContext)(Instance As ILifetimeScope) As TypedParameter
Return TypedParameter.From(Instance.BootStrapper(Of TContext))
End Function
<Extension>
Private Function CommandParameter(Of TContext As DbContext)(Instance As ILifetimeScope) As TypedParameter
Return TypedParameter.From(Instance.Command(Of TContext))
End Function
End Module
是这样叫的:
<Fact>
Sub Process_Runner_Should_Have_Correct_Arguments()
Dim oProcessRunner As IProcessRunner
Using oScope As ILifetimeScope = Me.Container.BeginLifetimeScope
' Arrange
' Act
oProcessRunner = oScope.ProcessRunner(Of SqliteContext)
' Assert
oProcessRunner.Command.Arguments.Update.Count.Should.Be(4)
oProcessRunner.Command.Arguments.Clean.Count.Should.Be(6)
End Using
End Sub
现在告诉我这是否不会让你头晕!
确实有效,但肯定有更好的方法。我愿意接受任何和所有改进建议。
是的,Harley,有更好的方法。哦-简单得多...
只需在传入参数列表中添加适当的
IContext
:
Dim oParameters As List(Of Parameter)
Dim oContext As IContext
If My.Environment = TheOneIWant
oContext = New SqliteContext(Me.SqliteOptions)
Else
oContext = New MsSqlContext(Me.MsSqlOptions)
End If
oParameters = New List(Of Parameter) From {
TypedParameter.From(oContext),
TypedParameter.From(SomethingElse)
}
Me.Container = GetContainer(oParameters)
报名方式:
Public Class DatabaseModule
Inherits BaseModule
Public Sub New(Parameters As List(Of Parameter))
MyBase.New(Parameters)
End Sub
Protected Overrides Sub Load(Builder As ContainerBuilder)
Dim oParameter As TypedParameter
oParameter = Me.
Parameters.
OfType(Of TypedParameter).
Single(Function(Parameter)
Return Parameter.Type = GetType(IContext)
End Function)
With Builder
.RegisterType(Of Repository).
WithParameters(Me.Parameters).
As(Of IRepository)()
.Register(Function(Context As IComponentContext) oParameter.Value).
InstancePerLifetimeScope.
As(Of IContext)()
End With
End Sub
End Class
这样容器中只有一个
IContext
来解析IRepository
,这意味着我们也可以消除多余的IRepository
。
我们也可以抛弃所有的扩展方法。哇!很好的摆脱。
当然这并不适合所有场景,但它至少适用于目前的test v.prod设置。
YMMV