Add type checking to constructor parameters (#9)
This commit is contained in:
Родитель
ed8bc6becb
Коммит
761be9e525
|
@ -108,7 +108,10 @@ namespace nanoFramework.DependencyInjection
|
|||
return lifetime + $"{nameof(ImplementationInstance)}: {ImplementationInstance}";
|
||||
}
|
||||
|
||||
internal Type GetImplementationType()
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Type"/> implementing the instance.
|
||||
/// </summary>
|
||||
public Type GetImplementationType()
|
||||
{
|
||||
if (ImplementationType != null)
|
||||
{
|
||||
|
|
|
@ -36,9 +36,9 @@ namespace nanoFramework.DependencyInjection
|
|||
/// </summary>
|
||||
internal void DisposeServices()
|
||||
{
|
||||
for (int i = Services.Count - 1; i >= 0; i--)
|
||||
for (int index = Services.Count - 1; index >= 0; index--)
|
||||
{
|
||||
if (Services[i].ImplementationInstance is IDisposable disposable)
|
||||
if (Services[index].ImplementationInstance is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
@ -160,7 +160,8 @@ namespace nanoFramework.DependencyInjection
|
|||
|
||||
if (constructorParameters == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
throw new InvalidOperationException(
|
||||
$"Constructor for '{implementationType}' could not be located.");
|
||||
}
|
||||
|
||||
if (constructorParameters.Length == 0)
|
||||
|
@ -176,10 +177,10 @@ namespace nanoFramework.DependencyInjection
|
|||
{
|
||||
var parameterType = constructorParameters[index].ParameterType;
|
||||
|
||||
if (TryBindToPrimitive(parameterType, out object defaultType))
|
||||
if (parameterType.IsResolvable())
|
||||
{
|
||||
types[index] = parameterType;
|
||||
parameters[index] = defaultType;
|
||||
parameters[index] = GetResolvableDefault(parameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -187,7 +188,8 @@ namespace nanoFramework.DependencyInjection
|
|||
|
||||
if (service == null)
|
||||
{
|
||||
throw new InvalidOperationException($"'{implementationType}'->'{parameterType}'.");
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to resolve service for '{parameterType}'.");
|
||||
}
|
||||
|
||||
types[index] = parameterType;
|
||||
|
@ -220,18 +222,34 @@ namespace nanoFramework.DependencyInjection
|
|||
{
|
||||
if (constructors[j].GetParameters().Length == constructors[j + 1].GetParameters().Length)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
throw new InvalidOperationException(
|
||||
$"Multiple constructors found in '{implementationType}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 2: get the constructor with the most parameters
|
||||
// step 2: get the constructor with the most resolvable parameters
|
||||
foreach (ConstructorInfo constructor in constructors)
|
||||
{
|
||||
ParameterInfo[] parameters = constructor.GetParameters();
|
||||
|
||||
int length = parameters.Length;
|
||||
|
||||
foreach (ParameterInfo parameter in parameters)
|
||||
{
|
||||
Type type = parameter.ParameterType;
|
||||
|
||||
if (type.IsResolvable())
|
||||
{
|
||||
// check for simple binding first
|
||||
}
|
||||
else if (GetService(type) == null)
|
||||
{
|
||||
// binding could not be resolved ingore constructor
|
||||
length = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestLength < length)
|
||||
{
|
||||
bestLength = length;
|
||||
|
@ -243,49 +261,30 @@ namespace nanoFramework.DependencyInjection
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try and bind to a primitive type.
|
||||
/// Get primitive default type.
|
||||
/// </summary>
|
||||
private static bool TryBindToPrimitive(Type type, out object defaultType)
|
||||
private static object GetResolvableDefault(Type type)
|
||||
{
|
||||
defaultType = null;
|
||||
|
||||
// This list dosn't match the binding list below because
|
||||
// This list dosn't match the IsResolvable() because
|
||||
// we only check for items we know are not null by default
|
||||
if (type == typeof(object)) defaultType = default;
|
||||
if (type == typeof(int)) defaultType = default(int);
|
||||
if (type == typeof(uint)) defaultType = default(uint);
|
||||
if (type == typeof(bool)) defaultType = default(bool);
|
||||
if (type == typeof(char)) defaultType = default(char);
|
||||
if (type == typeof(byte)) defaultType = default(byte);
|
||||
if (type == typeof(sbyte)) defaultType = default(sbyte);
|
||||
if (type == typeof(short)) defaultType = default(short);
|
||||
if (type == typeof(ushort)) defaultType = default(ushort);
|
||||
if (type == typeof(long)) defaultType = default(long);
|
||||
if (type == typeof(ulong)) defaultType = default(ulong);
|
||||
if (type == typeof(double)) defaultType = default(double);
|
||||
if (type == typeof(Guid)) defaultType = default(Guid);
|
||||
if (type == typeof(DateTime)) defaultType = default(DateTime);
|
||||
if (type == typeof(TimeSpan)) defaultType = default(TimeSpan);
|
||||
if (type == typeof(object)) return default;
|
||||
if (type == typeof(int)) return default(int);
|
||||
if (type == typeof(uint)) return default(uint);
|
||||
if (type == typeof(bool)) return default(bool);
|
||||
if (type == typeof(char)) return default(char);
|
||||
if (type == typeof(byte)) return default(byte);
|
||||
if (type == typeof(sbyte)) return default(sbyte);
|
||||
if (type == typeof(short)) return default(short);
|
||||
if (type == typeof(ushort)) return default(ushort);
|
||||
if (type == typeof(long)) return default(long);
|
||||
if (type == typeof(ulong)) return default(ulong);
|
||||
if (type == typeof(double)) return default(double);
|
||||
if (type == typeof(float)) return default(float);
|
||||
if (type == typeof(Guid)) return default(Guid);
|
||||
if (type == typeof(DateTime)) return default(DateTime);
|
||||
if (type == typeof(TimeSpan)) return default(TimeSpan);
|
||||
|
||||
return type == typeof(object)
|
||||
|| type == typeof(string)
|
||||
|| type == typeof(int)
|
||||
|| type == typeof(uint)
|
||||
|| type == typeof(bool)
|
||||
|| type == typeof(char)
|
||||
|| type == typeof(byte)
|
||||
|| type == typeof(sbyte)
|
||||
|| type == typeof(short)
|
||||
|| type == typeof(ushort)
|
||||
|| type == typeof(long)
|
||||
|| type == typeof(ulong)
|
||||
|| type == typeof(double)
|
||||
|| type == typeof(Guid)
|
||||
|| type == typeof(DateTime)
|
||||
|| type == typeof(TimeSpan)
|
||||
|| type == typeof(Enum)
|
||||
|| type == typeof(Array)
|
||||
|| type == typeof(ArrayList);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace nanoFramework.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods for <see cref="Type"/>.
|
||||
/// </summary>
|
||||
internal static class TypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares this instance to a specified type and returns an indication if resolvable value.
|
||||
/// </summary>
|
||||
/// <param name="type">The current <see cref="Type"/></param>
|
||||
public static bool IsResolvable(this Type type)
|
||||
{
|
||||
return type == typeof(string)
|
||||
|| type == typeof(bool)
|
||||
|| type == typeof(byte)
|
||||
|| type == typeof(sbyte)
|
||||
|| type == typeof(short)
|
||||
|| type == typeof(ushort)
|
||||
|| type == typeof(int)
|
||||
|| type == typeof(uint)
|
||||
|| type == typeof(long)
|
||||
|| type == typeof(ulong)
|
||||
|| type == typeof(double)
|
||||
|| type == typeof(float)
|
||||
|| type == typeof(object)
|
||||
|| type == typeof(DateTime)
|
||||
|| type == typeof(TimeSpan)
|
||||
|| type == typeof(Guid)
|
||||
|| type == typeof(Array)
|
||||
|| type == typeof(ArrayList);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@
|
|||
<Compile Include="DependencyInjection\ActivatorUtilities.cs" />
|
||||
<Compile Include="DependencyInjection\IServiceCollection.cs" />
|
||||
<Compile Include="DependencyInjection\ServiceProviderEngine.cs" />
|
||||
<Compile Include="DependencyInjection\TypeExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="DependencyInjection\ServiceCollection.cs" />
|
||||
<Compile Include="DependencyInjection\ServiceCollectionContainerBuilderExtensions.cs" />
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
<packages>
|
||||
<package id="nanoFramework.CoreLibrary" version="1.12.0" targetFramework="netnano1.0" />
|
||||
<package id="Nerdbank.GitVersioning" version="3.5.107" developmentDependency="true" targetFramework="netnano1.0" />
|
||||
</packages>
|
||||
</packages>
|
||||
|
|
|
@ -43,9 +43,6 @@ namespace nanoFramework.DependencyInjection.UnitTests
|
|||
// Assert.IsType(typeof(StructFakeService), service.GetType());
|
||||
//}
|
||||
|
||||
//TODO: Reflection is not resolving with the IsArray flag
|
||||
// https://github.com/nanoframework/Home/issues/1086
|
||||
|
||||
[TestMethod]
|
||||
public void ServiceInstanceWithPrimitiveBinding()
|
||||
{
|
||||
|
@ -55,13 +52,28 @@ namespace nanoFramework.DependencyInjection.UnitTests
|
|||
|
||||
var service = (ClassWithPrimitiveBinding)serviceProvider.GetService(typeof(ClassWithPrimitiveBinding));
|
||||
|
||||
Assert.Null(service.Obj);
|
||||
Assert.Null(service.Str);
|
||||
Assert.Equal(0, service.Integer);
|
||||
Assert.Equal(false, service.Boolean);
|
||||
//Assert.Null(service.Characters); // array is not resolving correctly
|
||||
Assert.Null(service.Obj);
|
||||
Assert.NotNull(service.Guid);
|
||||
Assert.NotNull(service.Time);
|
||||
|
||||
Assert.True(service.Boolean == false);
|
||||
Assert.True(service.Short == 0);
|
||||
Assert.True(service.Ushort == 0);
|
||||
Assert.True(service.Int == 0);
|
||||
Assert.True(service.UInt == 0);
|
||||
Assert.True(service.Long == 0);
|
||||
Assert.True(service.Ulong == 0);
|
||||
Assert.True(service.Double == 0);
|
||||
Assert.True(service.Float == 0);
|
||||
Assert.True(service.Byte == new byte());
|
||||
Assert.True(service.SByte == new sbyte());
|
||||
Assert.True(service.DateTime == new DateTime());
|
||||
Assert.True(service.TimeSpan == new TimeSpan());
|
||||
Assert.True(service.Array == default);
|
||||
Assert.True(service.ArrayList == default);
|
||||
|
||||
//TODO: Add array types - reflection is not resolving with the IsArray flag
|
||||
// https://github.com/nanoframework/Home/issues/1086
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -212,6 +224,19 @@ namespace nanoFramework.DependencyInjection.UnitTests
|
|||
);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IngoreUnresolvedMultipleConstructorMatches()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddSingleton(typeof(IFakeService), typeof(FakeService))
|
||||
.AddSingleton(typeof(ClassWithMultipleCtors))
|
||||
.BuildServiceProvider();
|
||||
|
||||
var service = (ClassWithMultipleCtors)serviceProvider.GetService(typeof(ClassWithMultipleCtors));
|
||||
|
||||
Assert.Equal(2, service.CtorUsed);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SelfResolveThenDispose()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace nanoFramework.DependencyInjection.UnitTests.Fakes
|
||||
{
|
||||
public class ClassWithMultipleCtors
|
||||
{
|
||||
public ClassWithMultipleCtors(string data)
|
||||
{
|
||||
CtorUsed = 0;
|
||||
}
|
||||
|
||||
public ClassWithMultipleCtors(IFakeService service, string data)
|
||||
{
|
||||
CtorUsed = 1;
|
||||
}
|
||||
|
||||
public ClassWithMultipleCtors(IFakeService service, string data1, int data2)
|
||||
{
|
||||
FakeService = service;
|
||||
Data1 = data1;
|
||||
Data2 = data2;
|
||||
|
||||
CtorUsed = 2;
|
||||
}
|
||||
|
||||
public ClassWithMultipleCtors(IFakeService service, ICollection collection, string data1, int data2)
|
||||
{
|
||||
FakeService = service;
|
||||
Data1 = data1;
|
||||
Data2 = data2;
|
||||
Collection = collection;
|
||||
CtorUsed = 3;
|
||||
}
|
||||
|
||||
public IFakeService FakeService { get; }
|
||||
|
||||
public string Data1 { get; }
|
||||
|
||||
public int Data2 { get; }
|
||||
|
||||
public int CtorUsed { get; set; }
|
||||
|
||||
public ICollection Collection { get; set; }
|
||||
}
|
||||
}
|
|
@ -2,35 +2,86 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace nanoFramework.DependencyInjection.UnitTests.Fakes
|
||||
{
|
||||
public class ClassWithPrimitiveBinding
|
||||
{
|
||||
public object Obj { get; set; }
|
||||
public string Str { get; set; }
|
||||
public int Integer { get; set; }
|
||||
public bool Boolean { get; set; }
|
||||
//public string[] Characters { get; set; }
|
||||
public Guid Guid { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
|
||||
public bool Boolean { get; set; }
|
||||
|
||||
public byte Byte { get; set; }
|
||||
|
||||
public sbyte SByte { get; set; }
|
||||
|
||||
public short Short { get; set; }
|
||||
|
||||
public ushort Ushort { get; set; }
|
||||
|
||||
public int Int { get; set; }
|
||||
|
||||
public uint UInt { get; set; }
|
||||
|
||||
public long Long { get; set; }
|
||||
|
||||
public ulong Ulong { get; set; }
|
||||
|
||||
public double Double { get; set; }
|
||||
|
||||
public float Float { get; set; }
|
||||
|
||||
public object Obj { get; set; }
|
||||
|
||||
public DateTime DateTime { get; set; }
|
||||
|
||||
public TimeSpan TimeSpan { get; set; }
|
||||
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
public Array Array { get; set; }
|
||||
|
||||
public ArrayList ArrayList { get; set; }
|
||||
|
||||
public ClassWithPrimitiveBinding(
|
||||
string str,
|
||||
bool boolean,
|
||||
byte @byte,
|
||||
sbyte sByte,
|
||||
short @short,
|
||||
ushort @ushort,
|
||||
int @int,
|
||||
uint uInt,
|
||||
long @long,
|
||||
ulong @ulong,
|
||||
double @double,
|
||||
float @float,
|
||||
object obj,
|
||||
string str,
|
||||
int integer,
|
||||
bool boolean,
|
||||
//string[] characters,
|
||||
DateTime dateTime,
|
||||
TimeSpan timeSpan,
|
||||
Guid guid,
|
||||
DateTime time)
|
||||
Array array,
|
||||
ArrayList arrayList)
|
||||
{
|
||||
Obj = obj;
|
||||
Str = str;
|
||||
Integer = integer;
|
||||
Boolean = boolean;
|
||||
//Characters = characters;
|
||||
Byte = @byte;
|
||||
SByte = sByte;
|
||||
Short = @short;
|
||||
Ushort = @ushort;
|
||||
Int = @int;
|
||||
UInt = uInt;
|
||||
Long = @long;
|
||||
Ulong = @ulong;
|
||||
Double = @double;
|
||||
Float = @float;
|
||||
Obj = obj;
|
||||
DateTime = dateTime;
|
||||
TimeSpan = timeSpan;
|
||||
Guid = guid;
|
||||
Time = time;
|
||||
Array = array;
|
||||
ArrayList = arrayList;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="ActivatorUtilitiesTests.cs" />
|
||||
<Compile Include="DependencyInjectionTests.cs" />
|
||||
<Compile Include="Fakes\ClassWithMultipleCtors.cs" />
|
||||
<Compile Include="Fakes\ClassWithPrimitiveBinding.cs" />
|
||||
<Compile Include="Fakes\CreationCountFakeService.cs" />
|
||||
<Compile Include="Fakes\ClassWithThrowingEmptyCtor.cs" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче