1
0
Форкнуть 0
This commit is contained in:
bytewizer 2022-06-19 13:02:26 -07:00
Родитель 24ac133afe
Коммит 4108cc1097
19 изменённых файлов: 307 добавлений и 119 удалений

2
.github/workflows/update-dependencies.yml поставляемый
Просмотреть файл

@ -29,4 +29,4 @@ jobs:
- name: Update dependencies
uses: nanoframework/nanodu@v1
with:
solutionsToCheck: 'nanoFramework.System.Net.WebSockets.sln'
solutionsToCheck: 'nanoFramework.DependencyInjection.sln'

197
README.md
Просмотреть файл

@ -1 +1,196 @@
# nanoFramework.DependencyInjection
![nanoFramework logo](https://raw.githubusercontent.com/nanoframework/Home/main/resources/logo/nanoFramework-repo-logo.png)
-----
# 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](https://dev.azure.com/nanoframework/nanoFramework.DependencyInjection/_apis/build/status/nanoframework.DependencyInjection?repoName=nanoframework%2FnanoFramework.DependencyInjection&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.DependencyInjection/_build/latest?definitionId=56&repoName=nanoframework%2FnanoFramework.DependencyInjection&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.DependencyInjection.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.DependencyInjection/) |
## Samples
[Dependency Injection Sample](https://github.com/nanoframework/Samples/tree/main/samples/DependencyInjection)
## Dependency Injection Container
A Dependency Injection (DI) Container provides functionality and automates many of the tasks involved in Object Composition, Interception, and Lifetime Management. Its 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.
## 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 retrive the object.
This API mirrors as close as possible the official .NET
[Microsoft.Extensions.DependencyInjection](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection). Exceptions are mainly derived from the lack of generics support in .NET nanoFramework.
### Object Composition
Define an object composition to create and couple.
```csharp
public interface IFakeObject { }
public class FakeObject : IFakeObject { }
public interface IFakeRootObject
{
IFakeObject FakeObject { get; }
}
public class FakeRootObject : IFakeRootObject
{
public IFakeObject FakeObject { get; protected set; }
public FakeRootObject(IFakeObject fakeObject)
{
FakeObject = fakeObject;
}
}
```
### Registering Services
Create a Service Collection and register singleton or transient type services to the collection.
```csharp
var serviceProvider = new ServiceCollection()
.AddSingleton(typeof(IFakeObject), typeof(FakeObject))
.AddSingleton(typeof(IFakeRootObject), typeof(FakeRootObject))
.BuildServiceProvider();
```
### Service Provider
Create a Service Provider to access the object graph composition.
```csharp
var service = (FakeRootObject)serviceProvider.GetService(typeof(IFakeRootObject));
```
## Example Application Container
```csharp
using System;
using System.Device.Gpio;
using System.Threading;
using nanoFramework.Logging.Debug;
using nanoFramework.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace DI
{
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
_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 readonly ILogger _logger;
private readonly GpioController _gpioController;
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);
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");
}
}
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](https://github.com/nanoframework/Home).
Join our Discord community [here](https://discord.gg/gCyBu8T).
## Credits
The list of contributors to this project can be found at [CONTRIBUTORS](https://github.com/nanoframework/Home/blob/main/CONTRIBUTORS.md).
## License
The **nanoFramework** Class Libraries are licensed under the [MIT license](LICENSE.md).
## 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](https://dotnetfoundation.org/code-of-conduct).
### .NET Foundation
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).

14
config/SignClient.json Normal file
Просмотреть файл

@ -0,0 +1,14 @@
{
"SignClient": {
"AzureAd": {
"AADInstance": "https://login.microsoftonline.com/",
"ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8",
"TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e"
},
"Service": {
"Url": "https://codesign.dotnetfoundation.org/",
"ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001"
}
}
}

1
config/filelist.txt Normal file
Просмотреть файл

@ -0,0 +1 @@
**/*.nanoFramework.DependencyInjection.*

Двоичные данные
nanoFramework.DependencyInjection.zip

Двоичный файл не отображается.

Просмотреть файл

@ -43,6 +43,7 @@ namespace nanoFramework.DependencyInjection
_serviceDescriptors.Add(new ServiceDescriptor(typeof(IServiceProvider), this));
}
/// <inheritdoc />
public void Dispose()
{
foreach (ServiceDescriptor descriptor in _serviceDescriptors)
@ -76,11 +77,16 @@ namespace nanoFramework.DependencyInjection
/// <inheritdoc />
public object[] GetService(Type[] serviceType)
{
if (serviceType.Length == 0)
if (serviceType == null)
{
throw new ArgumentNullException(nameof(serviceType));
}
if (serviceType.Length == 0)
{
throw new ArgumentException(nameof(serviceType));
}
if (serviceType.Length == 1)
{
var services = GetServices(serviceType[0]);
@ -92,20 +98,20 @@ namespace nanoFramework.DependencyInjection
return new object[0];
}
object[] response = new object[0];
object[] array = new object[0];
foreach (Type type in serviceType)
{
var services = GetServices(type);
if (services.Length > 0)
{
var newResponse = new object[response.Length + services.Length];
Array.Copy(response, newResponse, response.Length);
Array.Copy(services, 0, newResponse, response.Length, services.Length);
response = newResponse;
var newResponse = new object[array.Length + services.Length];
Array.Copy(array, newResponse, array.Length);
Array.Copy(services, 0, newResponse, array.Length, services.Length);
array = newResponse;
}
}
return response.Length != 0 ? response : new object[0];
return array.Length != 0 ? array : new object[0];
}
private object[] GetServices(Type serviceType)
@ -149,22 +155,9 @@ namespace nanoFramework.DependencyInjection
private object Resolve(Type implementationType)
{
ConstructorInfo[] constructor;
ParameterInfo[] constructorParameters;
try
{
constructor = implementationType.GetConstructors();
constructorParameters = constructor[0].GetParameters();
}
catch
{
throw new InvalidOperationException(
$"A suitable constructor for type '{implementationType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.");
}
object instance;
ParameterInfo[] constructorParameters = GetParameters(implementationType);
if (constructorParameters.Length == 0)
{
instance = Activator.CreateInstance(implementationType);
@ -195,6 +188,20 @@ namespace nanoFramework.DependencyInjection
return instance;
}
private ParameterInfo[] GetParameters(Type implementationType)
{
try
{
ConstructorInfo[] constructor = implementationType.GetConstructors();
return constructor[0].GetParameters();
}
catch
{
throw new InvalidOperationException(
$"A suitable constructor for type '{implementationType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.");
}
}
private void ValidateService(ServiceDescriptor descriptor)
{
// TODO: Add service validator

Просмотреть файл

@ -5,35 +5,19 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CSharp.TestApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CSharp.TestApplication")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyTitle("nanoFramework.DependencyInjection")]
[assembly: AssemblyCompany("nanoFramework Contributors")]
[assembly: AssemblyProduct(".NET nanoframework nanoFramework.DependencyInjection")]
[assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")]
//////////////////////////////////////////////////////
// this assembly does NOT have a native counterpart //
//////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
[assembly: AssemblyNativeVersion("0.0.0.0")]
////////////////////////////////////////////////////////////////
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
/////////////////////////////////////////////////////////////////
// This attribute is mandatory when building Interop libraries //
// update this whenever the native assembly signature changes //
[assembly: AssemblyNativeVersion("1.0.0.0")]
/////////////////////////////////////////////////////////////////
[assembly: ComVisible(false)]

Просмотреть файл

@ -2,14 +2,14 @@
using Microsoft.Extensions.Logging;
namespace DependencyInjection
namespace DI
{
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;
@ -21,7 +21,7 @@ namespace DependencyInjection
public void Run()
{
var ledPin = 15; // Set pin number to blink
var ledPin = 23; // Set pin number to blink 15=ESP32; 23=STM32
_logger.LogInformation($"Started blinking led on pin {ledPin}.");
_hardware.StartBlinking(ledPin);

Просмотреть файл

@ -29,7 +29,7 @@
<HintPath>..\packages\nanoFramework.CoreLibrary.1.12.0\lib\mscorlib.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.Logging">
<HintPath>..\..\packages\nanoFramework.Logging.1.0.1.27\lib\nanoFramework.Logging.dll</HintPath>
<HintPath>..\..\packages\nanoFramework.Logging.1.0.1.29\lib\nanoFramework.Logging.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.Runtime.Events">
<HintPath>..\..\packages\nanoFramework.Runtime.Events.1.10.0\lib\nanoFramework.Runtime.Events.dll</HintPath>

Просмотреть файл

@ -4,7 +4,7 @@ using System.Device.Gpio;
using Microsoft.Extensions.Logging;
namespace DependencyInjection
namespace DI
{
internal class HardwareService : IHardwareService, IDisposable
{

Просмотреть файл

@ -1,4 +1,4 @@
namespace DependencyInjection
namespace DI
{
public interface IHardwareService
{

Просмотреть файл

@ -1,18 +1,19 @@
using Microsoft.Extensions.Logging;
using nanoFramework.Logging.Debug;
using nanoFramework.DependencyInjection;
namespace DependencyInjection
using Microsoft.Extensions.Logging;
namespace DI
{
public class Program
{
public static void Main()
{
var serviceProvider = ConfigureServices();
var application = (Application)serviceProvider.GetRequiredService(typeof(Application));
var services = ConfigureServices();
var application = (Application)services.GetRequiredService(typeof(Application));
application.Run();
}
private static ServiceProvider ConfigureServices()

Просмотреть файл

@ -1,33 +1,22 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CSharp.BlankApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CSharp.BlankApplication")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyTitle("nanoFramework.DependencyInjection.Sample")]
[assembly: AssemblyCompany("nanoFramework Contributors")]
[assembly: AssemblyProduct(".NET nanoframework nanoFramework.DependencyInjection Sample")]
[assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")]
//////////////////////////////////////////////////////
// this assembly does NOT have a native counterpart //
//////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
[assembly: AssemblyNativeVersion("0.0.0.0")]
////////////////////////////////////////////////////////////////
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="nanoFramework.CoreLibrary" version="1.12.0" targetFramework="netnano1.0" />
<package id="nanoFramework.Logging" version="1.0.1.27" targetFramework="netnano1.0" />
<package id="nanoFramework.Logging" version="1.0.1.29" targetFramework="netnano1.0" />
<package id="nanoFramework.Runtime.Events" version="1.10.0" targetFramework="netnano1.0" />
<package id="nanoFramework.System.Device.Gpio" version="1.0.4" targetFramework="netnano1.0" />
</packages>

Просмотреть файл

@ -23,20 +23,18 @@ namespace nanoFramework.DependencyInjection.UnitTests
Assert.NotSame(service1, service2);
}
// TODO: I think Array.Copy is failing to copy a struct type.
//[TestMethod]
//public void ServicesRegisteredWithImplementationTypeForStructTransientServices()
//public void ServicesRegisteredWithImplementationTypeForStructSingletonServices()
//{
// var serviceProvider = new ServiceCollection()
// .AddSingleton(typeof(PocoClass))
// .AddTransient(typeof(IStructFakeService), typeof(StructFakeService))
// .AddSingleton(typeof(IStructFakeService), typeof(StructFakeService))
// .BuildServiceProvider();
// var service1 = serviceProvider.GetService(typeof(IStructFakeService));
// var service2 = serviceProvider.GetService(typeof(IStructFakeService));
// var service = serviceProvider.GetService(typeof(IStructFakeService));
// Assert.IsType(typeof(StructFakeService), service1.GetType());
// Assert.IsType(typeof(StructFakeService), service2.GetType());
// Assert.NotSame(service1, service2);
// Assert.IsType(typeof(StructFakeService), service.GetType());
//}
[TestMethod]
@ -95,11 +93,14 @@ namespace nanoFramework.DependencyInjection.UnitTests
.BuildServiceProvider();
var rootService = (RootService)serviceProvider.GetRequiredService(typeof(IRootService));
Assert.IsType(typeof(Service1), rootService.Service1.GetType());
Assert.IsType(typeof(Service2), rootService.Service2.GetType());
Assert.Equal(2000, rootService.IntProperty);
Assert.Equal("default", rootService.StringProperty);
var innerService = (Service3)serviceProvider.GetRequiredService(typeof(IService3));
Assert.NotNull(innerService);
}
[TestMethod]
@ -142,7 +143,7 @@ namespace nanoFramework.DependencyInjection.UnitTests
object[] service = serviceProvider.GetService(new Type[] { typeof(IFakeService) });
Assert.NotNull(service);
Assert.Equals(1, service.Length);
Assert.Equal(1, service.Length);
Assert.IsType(typeof(FakeService), service[0].GetType());
}
@ -170,6 +171,7 @@ namespace nanoFramework.DependencyInjection.UnitTests
Assert.NotNull(service);
serviceProvider.Dispose();
}
[TestMethod]

Просмотреть файл

@ -4,5 +4,8 @@ namespace nanoFramework.DependencyInjection.UnitTests.Fakes
{
public class PocoClass
{
public PocoClass()
{
}
}
}

Просмотреть файл

@ -5,27 +5,19 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCopyright("Copyright (c) 2021 nanoFramework contributors")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyTitle("nanoFramework.DependencyInjection.Tests")]
[assembly: AssemblyCompany("nanoFramework Contributors")]
[assembly: AssemblyProduct(".NET nanoframework nanoFramework.DependencyInjection Tests")]
[assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")]
//////////////////////////////////////////////////////
// this assembly does NOT have a native counterpart //
//////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
[assembly: AssemblyNativeVersion("0.0.0.0")]
////////////////////////////////////////////////////////////////
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]

Просмотреть файл

@ -14,8 +14,8 @@ namespace nanoFramework.DependencyInjection.UnitTests
{
var serviceDescriptor = ServiceDescriptor.Describe(typeof(IFakeObject), typeof(FakeObject), ServiceLifetime.Singleton);
Assert.True(serviceDescriptor.ServiceType.GetType() == typeof(IFakeObject));
Assert.True(serviceDescriptor.ImplementationType.GetType() == typeof(FakeObject));
Assert.IsType(typeof(IFakeObject), serviceDescriptor.ServiceType.GetType());
Assert.IsType(typeof(FakeObject), serviceDescriptor.ImplementationType.GetType());
Assert.True(serviceDescriptor.Lifetime == ServiceLifetime.Singleton);
}

Просмотреть файл

@ -1,12 +1,12 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0.1",
"version": "1.0.0",
"assemblyVersion": {
"precision": "revision"
},
"semVer1NumericIdentifierPadding": 3,
"nuGetPackageVersion": {
"semVer": 2.0,
"semVer": 1.0,
"precision": "revision"
},
"publicReleaseRefSpec": [