Skip to content

📦 nanoFramework.DependencyInjection library for .NET nanoFramework

License

Notifications You must be signed in to change notification settings

nanoframework/nanoFramework.DependencyInjection

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Quality Gate Status Reliability Rating License NuGet #yourfirstpr Discord

nanoFramework logo


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 Build Status NuGet

Samples

Dependency Injection 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.