简体   繁体   中英

StructureMap Autowiring with two different instances of the same interface

For the last two days, i tried my best to learn something about StructureMap, using an old project of mine as an concrete implementation example. I tried to simplify my question as much as possible. While i will post my examples in vb.net, answers with examples in C# are also okay.

The project includes an interfaces called IDatabase which connects itself to a Database. The important part looks like this.

Public Interface IDatabase
    Function Connect(ByVal ConnectionSettings As ConnectionSettings) As Boolean
    ReadOnly Property ConnectionOpen As Boolean
    [... more functions...]
End Interface

Public Class MSSQLConnection
    Implements IDatabase
    Public Function Connect(ByVal ConnectionSettings As ConnectionSettings) As Boolean Implements IDatabase.Connect

       [... Implementation ...]

    End Function

[... more implementations...]
End Class

ConnectionSettings is a structure that has all the information needed to connect to a Database.

I want to open the Database Connection once and use it for every single connection in the project, so i register a instance in the ObjectFactory.

dim foo = ObjectFactory.GetInstance(Of MSSQLConnection)()
dim bar as ConnectionSettings
foo.connect(bar)
ObjectFactory.Configure(Sub(x) x.For(Of IDatabase).Use(foo))

Up until this part, everything works like a charm. Now, i get to a point where i hav e classes that need an additional instance of IDatabase because they connect to a second database.

Public Class ExampleClass
Public Sub New(ByVal SameOldDatabase as IDatabase, ByVal NewDatabase as IDatabase)
[...] Magic happens here [...]
End Sub
End Class

I want this second IDatabase to behave much like the first one. I want it to use a concrete, single instance and want to connect it to a different database invoking Connect with a different ConnectionSettings.

The problem is: While i'm pretty sure it's somewhow possible, (my initial idea was registering ExampleClass with alternative constructor arguments), i actually want to do it without registering ExampleClass. This probably involves more configuration, but i have no idea how to do it.

So, basically, it comes down to this question: How do i configurate the ObjectFactory in a way that the autowiring always invokes the constructor with object Database1 for the first IDatabase parameter and object Database2 for the second one (if there is one?)

You could use a RegistrationConvention and a named instance for the second connection. Consider the following quick'n'dirty code:

Imports StructureMap.Graph
Imports StructureMap.Configuration.DSL
Imports StructureMap

Public Module Module1

    Public Interface IDatabase
        Property ConString As String
    End Interface

    Public Class MSSQLConnection
        Implements IDatabase
        Public Property ConString() As String Implements IDatabase.ConString
    End Class

    Public Class ExampleClass
        Public Sub New(ByVal SameOldDatabase As IDatabase, ByVal NewDatabase As IDatabase)
            Console.WriteLine(SameOldDatabase.ConString)
            Console.WriteLine(NewDatabase.ConString)
        End Sub
    End Class

    Public Class SecondDatabaseConstructorIsAnotherOne
        Implements IRegistrationConvention

        Public Sub Process(ByVal type As Type, ByVal registry As Registry) Implements IRegistrationConvention.Process
            Dim ctor = type.GetConstructors().FirstOrDefault(Function(c) c.GetParameters().Where(Function(p) p.ParameterType = GetType(IDatabase)).Count = 2)
            If Not ctor Is Nothing Then

                Dim parameter = New List(Of Object)

                Dim second = False

                For Each o In ctor.GetParameters()
                    If o.ParameterType = GetType(IDatabase) AndAlso second Then
                        parameter.Add(ObjectFactory.GetNamedInstance(Of IDatabase)("secondDB"))
                    Else
                        If o.ParameterType = GetType(IDatabase) Then second = True
                        parameter.Add(ObjectFactory.GetInstance(o.ParameterType))
                    End If
                Next

                registry.For(type).Use(Function(context) Activator.CreateInstance(type, parameter.ToArray()))

            End If
        End Sub

    End Class

    Sub Main()

        Dim con1 = New MSSQLConnection() With {.ConString = "ConnectToFirstDatabase"}
        Dim con2 = New MSSQLConnection() With {.ConString = "ConnectToSecondDatabase"}

        ObjectFactory.Initialize(Sub(init)
                                     init.For(Of IDatabase).Use(con1)
                                     init.For(Of IDatabase).Add(con2).Named("secondDB")
                                 End Sub)

        ObjectFactory.Configure(Sub(config)
                                    config.Scan(Sub(scan)
                                                    scan.TheCallingAssembly()
                                                    scan.Convention(Of SecondDatabaseConstructorIsAnotherOne)()
                                                End Sub)
                                End Sub)

        ObjectFactory.GetInstance(Of ExampleClass)()
        Console.ReadLine()
    End Sub

End Module

You'll get the idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM