11 KiB
Welcome to the .NET nanoFramework Dependency Injection Library repository
Provides Dependency Injection (DI) for Inversion of Control (IoC) between classes and their dependencies built for .NET nanoFramework.
Build status
Component | Build Status | NuGet Package |
---|---|---|
nanoFramework.DependencyInjection |
Samples
Dependency Injection Unit Tests
Dependency Injection Container
A Dependency Injection (DI) Container provides functionality and automates many of the tasks involved in Object Composition, Interception, and Lifetime Management. It's an engine that resolves and manages object graphs. These DI Containers depend on the static information compiled into all classes. Then using reflection they can analyze the requested class and figure out which Dependencies are required.
This API mirrors as close as possible the official .NET DependencyInjection. Exceptions are mainly derived from the lack of generics support in .NET nanoFramework.
The .NET nanoFramework Generic Host provides convenience methods for creating dependency injection (DI) application containers with preconfigured defaults.
Usage
Service Collection
Creating a dependency injection container required three basic components.
- Object Composition - A object composition defining a set of objects to create and couple.
- Registering Services - Define an instance of the ServiceCollection and register the object composition with a specific service lifetime.
- Service Provider - Creating a service provider to retrieve the object.
Object Composition
Define an object composition to create and couple.
Note: You can not currently define your objects as 'struct' or include array type parameters (ie. byte[] bytes) as constructor parameters. Doing so will create a null execption when trying to activate object.
public class RootObject
{
public int One { get; }
public string Two { get; }
public ServiceObject ServiceObject { get; protected set; }
public RootObject(ServiceObject serviceObject)
{
ServiceObject = serviceObject;
}
// constructor with the most parameters will be used for activation
public RootObject(ServiceObject serviceObject, int one, string two)
{
ServiceObject = serviceObject;
One = one;
Two = two;
}
}
public class ServiceObject
{
public string Three { get; set; }
}
Registering Services
Create a Service Collection and register singleton or transient type services to the collection.
var serviceProvider = new ServiceCollection()
.AddSingleton(typeof(ServiceObject))
.AddSingleton(typeof(RootObject))
.BuildServiceProvider();
Service Provider
Create a Service Provider to access or update an object.
var service = (RootObject)serviceProvider.GetService(typeof(RootObject));
service.ServiceObject.Three = "3";
Create a scoped Service Provider providing convient access to crate and distroy scoped object.
var serviceProvider = new ServiceCollection()
.AddScoped(typeof(typeof(ServiceObject))
.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetServices(typeof(ServiceObject));
service.ServiceObject.Three = "3";
}
Activator Utilities
An instance of an object can be created by calling its constructor with any dependencies resolved through the service provider. Automatically instantiate a type with constructor arguments provided from an IServiceProvider without having to register the type with the DI Container.
var instance = (RootObject)ActivatorUtilities.CreateInstance(
serviceProvider, typeof(RootObject), 1, "2"
);
Debug.WriteLine($"One: {instance.One}");
Debug.WriteLine($"Two: {instance.Two}");
Debug.WriteLine($"Three: {instance.ServiceObject.Three}");
Debug.WriteLine($"Name: {instance.ServiceObject.GetType().Name}");
Validate On Build
A check is performed to ensure that all services registered with the container can actually be created. This can be particularly useful during development to fail fast and allow developers to fix the issue. Validate on build is configured false by default.
var serviceProvider = new ServiceCollection()
.AddSingleton(typeof(IServiceObject), typeof(ServiceObject))
.BuildServiceProvider(new ServiceProviderOptions() { ValidateOnBuild = true });
Validate Scopes
A check verifying that scoped services never gets resolved from root provider. Validate on build is configured false by default.
var serviceProvider = new ServiceCollection()
.AddSingleton(typeof(IServiceObject), typeof(ServiceObject))
.BuildServiceProvider(new ServiceProviderOptions() { ValidateScopes = true });
Example Application Container
using System;
using System.Device.Gpio;
using System.Threading;
using nanoFramework.Logging.Debug;
using nanoFramework.DependencyInjection;
using Microsoft.Extensions.Logging;
nanoFramework.DiApplication
{
public class Program
{
public static void Main()
{
var services = ConfigureServices();
var application = (Application)services.GetRequiredService(typeof(Application));
application.Run();
}
private static ServiceProvider ConfigureServices()
{
return new ServiceCollection()
.AddSingleton(typeof(Application))
.AddSingleton(typeof(IHardwareService), typeof(HardwareService))
.AddSingleton(typeof(ILoggerFactory), typeof(DebugLoggerFactory))
.BuildServiceProvider();
}
}
internal class Application
{
private readonly ILogger _logger;
private readonly IHardwareService _hardware;
private readonly IServiceProvider _provider;
public Application(
IServiceProvider provider,
IHardwareService hardware,
ILoggerFactory loggerFactory)
{
_provider = provider;
_hardware = hardware;
_logger = loggerFactory.CreateLogger(nameof(Application));
_logger.LogInformation("Initializing application...");
}
public void Run()
{
var ledPin = 23; // Set pin number to blink 15=ESP32; 23=STM32
_logger.LogInformation($"Started blinking led on pin {ledPin}.");
_hardware.StartBlinking(ledPin);
}
}
internal interface IHardwareService
{
public void StartBlinking(int ledPin) { }
}
internal class HardwareService : IHardwareService, IDisposable
{
private Thread _thread;
private readonly ILogger _logger;
private readonly GpioController _gpioController;
public HardwareService()
{
_gpioController = new GpioController();
var loggerFactory = new DebugLoggerFactory();
_logger = loggerFactory.CreateLogger(nameof(HardwareService));
}
public HardwareService(ILoggerFactory loggerFactory)
{
_gpioController = new GpioController();
_logger = loggerFactory.CreateLogger(nameof(HardwareService));
}
public void StartBlinking(int ledPin)
{
GpioPin led = _gpioController.OpenPin(ledPin, PinMode.Output);
led.Write(PinValue.Low);
_thread = new Thread(() =>
{
while (true)
{
Thread.Sleep(2000);
led.Write(PinValue.High);
_logger.LogInformation("Led status: on");
Thread.Sleep(2000);
led.Write(PinValue.Low);
_logger.LogInformation("Led status: off");
}
});
_thread.Start();
}
public void Dispose()
{
_gpioController.Dispose();
}
}
}
Feedback and documentation
For documentation, providing feedback, issues and finding out how to contribute please refer to the Home repo.
Join our Discord community here.
Credits
The list of contributors to this project can be found at CONTRIBUTORS.
License
The nanoFramework Class Libraries are licensed under the MIT license.
Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behaviour in our community. For more information see the .NET Foundation Code of Conduct.
.NET Foundation
This project is supported by the .NET Foundation.