Autofac:如何控制在内部解析链中将哪个实例发送到依赖项?

问题描述 投票:0回答:1

我有这些课程:

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

现在告诉我这是否不会让你头晕!

确实有效,但肯定有更好的方法。我愿意接受任何和所有改进建议。

dependency-injection autofac
1个回答
0
投票

是的,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

© www.soinside.com 2019 - 2024. All rights reserved.