IoC via DI - Inversion of Control via Dependency Injection with H.Necessaire
H.Necessaire's mechanism for Inversion of Control via Dependency Injection
NuGets
dotnet add package H.Necessaire
Basics
public interface ImADataGenerator
{
Task<int[]> GenerateIntegers();
}
Define an operation contract (aka interface)
internal class StaticDataGenerator : ImADataGenerator
{
public Task<int[]> GenerateIntegers()
{
return 0.AsArray().AsTask();
}
}
Write a concrete implementation for the contract
ImADependencyRegistry depsRegistry = H.Necessaire.IoC.NewDependencyRegistry();
depsRegistry.Register<ImADataGenerator>(() => new BLL.Concrete.StaticDataGenerator());
Register the concrete implementation
ImADependencyProvider depsProvider = depsRegistry;
ImADataGenerator dataGenerator = depsProvider.Get<ImADataGenerator>();
int[] integers = await dataGenerator.GenerateIntegers();
Inject and use the dependency
Grouping
internal class DependencyGroup : ImADependencyGroup
{
public void RegisterDependencies(ImADependencyRegistry dependencyRegistry)
{
dependencyRegistry.Register<ImADataGenerator>(() => new Concrete.StaticDataGenerator());
}
}
Define a Dependency Group by implementing ImADependencyGroup
ImADependencyRegistry depsRegistry = H.Necessaire.IoC.NewDependencyRegistry();
depsRegistry.Register<BLL.DependencyGroup>(() => new BLL.DependencyGroup());
Register the group with the container
Multiple Implementations
[ID("random")]
[Alias("rnd")]
internal class RandomDataGenerator : ImADataGenerator
{
public Task<int[]> GenerateIntegers()
{
return
Enumerable
.Range(0, Random.Shared.Next(1, 10))
.Select(i => Random.Shared.Next())
.ToArray()
.AsTask()
;
}
}
[ID("static")]
[Alias("st")]
internal class StaticDataGenerator : ImADataGenerator
{
public Task<int[]> GenerateIntegers()
{
return 0.AsArray().AsTask();
}
}
Declare multiple concrete implementations
ImADataGenerator staticDataGenerator = depsProvider.Build<ImADataGenerator>("static");
ImADataGenerator randomDataGenerator = depsProvider.Build<ImADataGenerator>("random");
Console.WriteLine(string.Join(", ", await staticDataGenerator.GenerateIntegers()));
Console.WriteLine(string.Join(", ", await randomDataGenerator.GenerateIntegers()));
Build/Resolve the desired implementation
Note that registering multiple implementations of the same type will overwrite existing registrations; they will not resolve to an enumeration of the given type.
Dependency Injection
Injecting dependencies into a class is done explicitly by implementing ImADependency
.
internal class ComposedDataGenerator : ImADataGenerator, ImADependency
Implement ImADependency
to inject dependencies into a class
[ID("composed")]
internal class ComposedDataGenerator : ImADataGenerator, ImADependency
{
ImADataGenerator staticDataGenerator;
ImADataGenerator randomDataGenerator;
public void ReferDependencies(ImADependencyProvider dependencyProvider)
{
staticDataGenerator = dependencyProvider.Build<ImADataGenerator>("static");
randomDataGenerator = dependencyProvider.Build<ImADataGenerator>("random");
}
public async Task<int[]> GenerateIntegers()
{
int[][] dataParts = await Task.WhenAll(
staticDataGenerator.GenerateIntegers(),
randomDataGenerator.GenerateIntegers()
);
return dataParts.SelectMany(part => part).ToArray();
}
}
Full example of a class with injected dependencies
All in all, the DI mechanism in H.Necessaire is kept very simple, as described above.
Any registration (via .Register<>()
) is singleton within the given container.
Transient registrations can also be defined via .RegisterAlwaysNew<>()
.
There is no scoping concept, but it can be easily achieved by creating a new container wherever needed.
The core dependencies of H.Necessaire are all bundled in HNecessaireDependencyGroup
.
Therefore you can simply add them to your container like so:
ImADependencyRegistry depsRegistry
= H.Necessaire.IoC
.NewDependencyRegistry()
.Register<HNecessaireDependencyGroup>(() => new HNecessaireDependencyGroup())
;
Adding the core dependencies of H.Necessaire to your container
But note that this is not required when you're using the AppWireup mechanisms or Runtime mechanism, since it's already done by them.