Multi-binding

Kodein-DI allows multi bindings via a binding set.

In a Set

Binding in a Set

To have multiple bindings in a set, you need to:

  • Declare that you are using a set binding for a particular bound type.

  • Add bindings to the set.

Example creating a set of Configuration bindings.
val di = DI {
    bindSet<Configuration> { (1)
        add { provider{ FooConfiguration() } } (2)
        bind { singleton { BarConfiguration() } } (3)
    }
}
1 Creating a set binding of Configuration.
2 adds a Configuration binding implementation using the convenience method.
3 adds a Configuration binding and attaches it to the DI container.

You can:

  • Use different binding types (such as provider or singleton) in the same set.

  • Add bindings to the same set in different modules if the set has been declared first.

Convenience Methods

Kodein-DI provides two types of convenience methods for adding bindings to sets:

add* Methods

The add* methods add bindings only to the set:

Example using add* convenience methods.
val di = DI {
    bindSet<Configuration> {
        addSingleton { FooConfiguration() }   (1)
        addProvider { BarConfiguration() }    (2)
        addInstance(existingConfig)           (3)
    }
}
1 Adds a singleton binding to the set (equivalent to add { singleton { FooConfiguration() } }).
2 Adds a provider binding to the set (equivalent to add { provider { BarConfiguration() } }).
3 Adds an instance binding to the set (equivalent to add { instance(existingConfig) }).
bind* Methods

The bind* methods add bindings to both the set AND the DI container, allowing retrieval by tag:

Example using bind* convenience methods with tags.
val di = DI {
    bindSet<Configuration> {
        bindSingleton(tag = "foo") { FooConfiguration() }      (1)
        bindProvider(tag = "bar") { BarConfiguration() }       (2)
        bindInstance(tag = "existing", instance = existingConfig) (3)
    }
}

// Retrieve from set
val allConfigs: Set<Configuration> by di.instance()

// Also retrieve individually by tag
val fooConfig: Configuration by di.instance(tag = "foo")
val barConfig: Configuration by di.instance(tag = "bar")
1 Adds a singleton to the set AND registers it in the container with tag "foo".
2 Adds a provider to the set AND registers it in the container with tag "bar".
3 Adds an instance to the set AND registers it in the container with tag "existing".

Use add* methods when you only need set bindings. Use bind* methods when you want both set membership and individual retrieval by tag.

Example creating a set of Configuration bindings and populates it from modules.
val module1 by DI.Module {
    inBindSet<Configuration> {
        addProvider { FooConfiguration() } (2)
        addSingleton { BarConfiguration() } (2)
    }
}
val di = DI {
    bindSet<Configuration>() (1)
    importAll(module1)
}
1 Creating a set binding of Configuration.
2 add multiple Configuration binding implementation using convenience methods.

You can also bind multiple bindings with arguments (such as factory or multiton) in a set as long as all bindings share the same argument type.

Example creating a set of Result bindings with add* methods.
val di = DI {
    bindArgSet<Query, Result> { (1)
        addFactory { q: Query -> Foo.query(q) } (2)
        addMultiton { q: Query -> Bar.query(q) } (3)
    }
}
1 Creating an argument set binding of Result for arguments of type Query.
2 Adds a factory binding using convenience method (equivalent to add { factory { q: Query → Foo.query(q) } }).
3 Adds a multiton binding using convenience method (equivalent to add { multiton { q: Query → Bar.query(q) } }).
Example using bind* methods with argument sets for tagged retrieval.
val di = DI {
    bindArgSet<Query, Result> {
        bindFactory(tag = "foo") { q: Query -> Foo.query(q) }     (1)
        bindMultiton(tag = "bar") { q: Query -> Bar.query(q) }    (2)
    }
}

// Retrieve from set
val results: Set<Result> by di.instance(arg = Query("SELECT * FROM USER;"))

// Also retrieve individually by tag
val fooFactory: (Query) -> Result by di.factory(tag = "foo")
val barMultiton: (Query) -> Result by di.factory(tag = "bar")
1 Adds a factory to the set AND registers it in the container with tag "foo".
2 Adds a multiton to the set AND registers it in the container with tag "bar".

Retrieving from a Set

Note that the type being bound is Set<T>, not T.
Therefore, you need to retrieve a Set:

Example retrieving set of Configuration.
val configurations: Set<Configuration> by di.instance()

To retrieve a Set with argument, thus going through a factory or a multiton, you need to pass the needed argument to the instance function.

Example retrieving set of Result with Query argument.
val result: Set<Result> by di.instance(arg = Query("SELECT * FROM USER;"))

In a map

Kodein-DI does not directly support map multi-binding. However, it is very easy to create a binding map by using a binding set.

First, create the following primitive:

Example of the type alias for a map multi-binding as Map<String, Configuration>.
typealias ConfigurationEntry = Pair<String, Configuration>
typealias ConfigurationEntries = Set<ConfigurationEntry>

Then, bind with keys:

Example binding as in a map multibinding.
val di = DI {
    bindSet<ConfigurationEntry> {
        add { singleton { "foo" to FooConfiguration() } }
        add { provider { "bar" to BarConfiguration() } }
    }
}

Finally, retrieve the map:

Example retrieving a map multibinding.
val configurations by di.instance<ConfigurationEntries>().toMap()