Sometimes it is necessary to determine which binding will be used to inject explicitly. To do this, use a special tag created by calling the Tag.On()
method. Tag on injection site is specified in a special format: Tag.On("<namespace>.<type>.<member>[:argument]")
. The argument is specified only for the constructor and methods. For example, for namespace MyNamespace and type Class1:
- `Tag.On("MyNamespace.Class1.Class1:state1") - the tag corresponds to the constructor argument named state of type MyNamespace.Class1
Tag.On("MyNamespace.Class1.DoSomething:myArg")
- the tag corresponds to the myArg argument of the DoSomething methodTag.On("MyNamespace.Class1.MyData")
- the tag corresponds to property or field MyData
The wildcards *
and ?
are supported. All names are case-sensitive. The global namespace prefix global::
must be omitted. You can also combine multiple tags in a single Tag.On("...", "...")
call.
For generic types, the type name also contains the number of type parameters, e.g., for the myDep
constructor argument of the Consumer<T>
class, the tag on the injection site would be MyNamespace.Consumer`1.Consumer:myDep
:
namespace MyNamespace;
interface IDependency;
class AbcDependency : IDependency;
class XyzDependency : IDependency;
class Consumer<T>(IDependency myDep)
{
public IDependency Dependency { get; } = myDep;
}
interface IService
{
IDependency Dependency1 { get; }
IDependency Dependency2 { get; }
IDependency Dependency3 { get; }
IDependency Dependency4 { get; }
}
class Service(
IDependency dependency1,
IDependency dependency2,
Consumer<string> consumer)
: IService
{
public IDependency Dependency1 { get; } = dependency1;
public IDependency Dependency2 { get; } = dependency2;
public required IDependency Dependency3 { init; get; }
public IDependency Dependency4 => consumer.Dependency;
}
DI.Setup(nameof(Composition))
.Bind(
Tag.On("MyNamespace.Service.Service:dependency1"),
// Tag on injection site for generic type
Tag.On("MyNamespace.Consumer`1.Consumer:myDep"))
.To<AbcDependency>()
.Bind(
// Combined tag
Tag.On(
"MyNamespace.Service.Service:dependency2",
"MyNamespace.Service:Dependency3"))
.To<XyzDependency>()
.Bind<IService>().To<Service>()
// Specifies to create the composition root named "Root"
.Root<IService>("Root");
var composition = new Composition();
var service = composition.Root;
service.Dependency1.ShouldBeOfType<AbcDependency>();
service.Dependency2.ShouldBeOfType<XyzDependency>();
service.Dependency3.ShouldBeOfType<XyzDependency>();
service.Dependency4.ShouldBeOfType<AbcDependency>();
Warning
Each potentially injectable argument, property, or field contains an additional tag. This tag can be used to specify what can be injected there. This will only work if the binding type and the tag match. So while this approach can be useful for specifying what to enter, it can be more expensive to maintain and less reliable, so it is recommended to use attributes like [Tag(...)]
instead.
Class diagram:
classDiagram
class Composition {
<<partial>>
+IService Root
}
Service --|> IService
class Service {
+Service(IDependency dependency1, IDependency dependency2, ConsumerᐸStringᐳ consumer)
+IDependency Dependency3
}
AbcDependency --|> IDependency
AbcDependency --|> IDependency
class AbcDependency {
+AbcDependency()
}
XyzDependency --|> IDependency
class XyzDependency {
+XyzDependency()
}
class ConsumerᐸStringᐳ {
+Consumer(IDependency myDep)
}
class IService {
<<interface>>
}
class IDependency {
<<interface>>
}
Composition ..> Service : IService Root
Service *-- AbcDependency : IDependency
Service *-- "2 " XyzDependency : IDependency
Service *-- ConsumerᐸStringᐳ : ConsumerᐸStringᐳ
ConsumerᐸStringᐳ *-- AbcDependency : IDependency