Compile time dependency injection for .NET.
Written as a Roslyn Source Generator, PHX.Inject will analyze dependency specifications defined in your code and generate the source code that performs the injection and linking at build time. This results in blazing fast injection at runtime, and quick identification of dependency issues at compile time.
PHX.Inject can be installed as a Nuget package using the .NET CLI.
dotnet add package Phx.Inject.Generator
The simplest set up consists of a Specification
and an Injector
.
using Phx.Inject;
[Specification]
internal static class TestSpecification {
[Factory]
internal static int GetIntValue() {
return 10;
}
}
[Injector(
typeof(TestSpecification)
)]
public interface ITestInjector {
public int GetInt();
}
The dependencies can be retrieved in code by instantiating the generated injector.
ITestInjector injector = new GeneratedTestInjector();
int myInt = injector.GetInt();
Verify.That(myInt.IsEqualTo(10));
Specifications contain Factories and Builders that define how dependencies should be linked and constructed. The Injector is the interface used to construct and access those dependencies.
The example above will generate a new class GeneratedTestInjector
that
implements the ITestInjector
interface. The GetInt()
method will be linked
to the TestSpecification.GetIntValue()
method based on the return type of the
methods.
The power of a Dependency Injection Framework comes from it's ability to define a graph of dependencies, one node at a time, and automatically resolve the links between each of those nodes.
using Phx.Inject;
[Specification]
internal static class TestSpecification {
[Factory]
internal static MyClass GetMyClass(int intValue) {
return new MyClass(intValue);
}
[Builder]
internal static void GetMyBuiltClass(MyBuiltClass target, int intValue) {
target.Value = intValue;
}
[Factory]
internal static int GetIntValue() {
return 10;
}
}
[Injector(
typeof(TestSpecification)
)]
public interface ITestInjector {
public MyClass GetMyClass();
public void Build(MyBuiltClass target);
}
In this example, the GetMyClass
factory in the specification accepts an
argument. This argument will be invoked with the linked int value dependency in
the generated injector.
This example also contains a builder. A builder is similar to a factory, except the instance is not constructed by the method, it is passed in and the method will set up properties on the object. This pattern is useful when you do not have control of the instantiation of the instance, but still want it configured with values from the dependency graph.
ITestInjector injector = new GeneratedTestInjector();
MyClass myClass = injector.GetMyClass();
MyBuiltClass myBuiltClass = new MyBuiltClass();
injector.Build(myBuiltClass);
Verify.That(myClass.Value.IsEqualTo(10));
Verify.That(myBuiltClass.Value.IsEqualTo(10));
See the Documentation for more details.
Licensed under the Apache License, Version 2.0.
See http://www.apache.org/licenses/LICENSE-2.0 for full license information.