#44: add validator to validate current setup of container

pull/57/head
Simon G 4 years ago
parent 7d849f5de4
commit 740e661cc2
  1. 24
      LightweightIocContainer/IocContainer.cs
  2. 1
      LightweightIocContainer/LightweightIocContainer.csproj
  3. 41
      LightweightIocContainer/LightweightIocContainer.xml
  4. 63
      LightweightIocContainer/Validation/IocValidator.cs
  5. 74
      Test.LightweightIocContainer/IocValidatorTest.cs

@ -25,15 +25,19 @@ namespace LightweightIocContainer
{ {
private readonly RegistrationFactory _registrationFactory; private readonly RegistrationFactory _registrationFactory;
private readonly List<IRegistration> _registrations = new List<IRegistration>();
private readonly List<(Type type, object instance)> _singletons = new List<(Type, object)>(); private readonly List<(Type type, object instance)> _singletons = new List<(Type, object)>();
private readonly List<(Type type, Type scope, ConditionalWeakTable<object, object> instances)> _multitons = new List<(Type, Type, ConditionalWeakTable<object, object>)>(); private readonly List<(Type type, Type scope, ConditionalWeakTable<object, object> instances)> _multitons = new List<(Type, Type, ConditionalWeakTable<object, object>)>();
/// <summary> /// <summary>
/// The main container that carries all the <see cref="IRegistration"/>s and can resolve all the types you'll ever want /// The main container that carries all the <see cref="IRegistration"/>s and can resolve all the types you'll ever want
/// </summary> /// </summary>
public IocContainer() => _registrationFactory = new RegistrationFactory(this); public IocContainer()
{
_registrationFactory = new RegistrationFactory(this);
Registrations = new List<IRegistration>();
}
internal List<IRegistration> Registrations { get; }
/// <summary> /// <summary>
/// Install the given installers for the current <see cref="IocContainer"/> /// Install the given installers for the current <see cref="IocContainer"/>
@ -221,14 +225,14 @@ namespace LightweightIocContainer
private void Register(IRegistration registration) private void Register(IRegistration registration)
{ {
//if type is already registered //if type is already registered
if (_registrations.Any(r => r.InterfaceType == registration.InterfaceType)) if (Registrations.Any(r => r.InterfaceType == registration.InterfaceType))
throw new MultipleRegistrationException(registration.InterfaceType); throw new MultipleRegistrationException(registration.InterfaceType);
//don't allow lifestyle.multiton without iMultitonRegistration //don't allow lifestyle.multiton without iMultitonRegistration
if (registration is ILifestyleProvider lifestyleProvider && lifestyleProvider.Lifestyle == Lifestyle.Multiton && !(registration is IMultitonRegistration)) if (registration is ILifestyleProvider lifestyleProvider && lifestyleProvider.Lifestyle == Lifestyle.Multiton && !(registration is IMultitonRegistration))
throw new InvalidRegistrationException("Can't register a type as Lifestyle.Multiton without a scope (Registration is not of type IMultitonRegistration)."); throw new InvalidRegistrationException("Can't register a type as Lifestyle.Multiton without a scope (Registration is not of type IMultitonRegistration).");
_registrations.Add(registration); Registrations.Add(registration);
} }
/// <summary> /// <summary>
@ -268,7 +272,7 @@ namespace LightweightIocContainer
/// <param name="resolveStack">The current resolve stack</param> /// <param name="resolveStack">The current resolve stack</param>
/// <returns>An instance of the given <see cref="Type"/></returns> /// <returns>An instance of the given <see cref="Type"/></returns>
/// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception> /// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception>
private object Resolve(Type type, object[] arguments, List<Type> resolveStack) => internal object Resolve(Type type, object[] arguments, List<Type> resolveStack) =>
GenericMethodCaller.Call(this, nameof(ResolveInternal), type, BindingFlags.NonPublic | BindingFlags.Instance, arguments, resolveStack); GenericMethodCaller.Call(this, nameof(ResolveInternal), type, BindingFlags.NonPublic | BindingFlags.Instance, arguments, resolveStack);
/// <summary> /// <summary>
@ -569,11 +573,11 @@ namespace LightweightIocContainer
[CanBeNull] [CanBeNull]
private IRegistration FindRegistration<T>() private IRegistration FindRegistration<T>()
{ {
IRegistration registration = _registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); IRegistration registration = Registrations.FirstOrDefault(r => r.InterfaceType == typeof(T));
if (registration != null) if (registration != null)
return registration; return registration;
registration = _registrations.OfType<ITypedRegistration>().FirstOrDefault(r => r.ImplementationType == typeof(T)); registration = Registrations.OfType<ITypedRegistration>().FirstOrDefault(r => r.ImplementationType == typeof(T));
if (registration != null) if (registration != null)
return registration; return registration;
@ -581,7 +585,7 @@ namespace LightweightIocContainer
if (!typeof(T).GenericTypeArguments.Any()) if (!typeof(T).GenericTypeArguments.Any())
return null; return null;
List<IRegistration> openGenericRegistrations = _registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList(); List<IRegistration> openGenericRegistrations = Registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList();
if (!openGenericRegistrations.Any()) if (!openGenericRegistrations.Any())
return null; return null;
@ -619,7 +623,7 @@ namespace LightweightIocContainer
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
_registrations.Clear(); Registrations.Clear();
_singletons.Clear(); _singletons.Clear();
_multitons.Clear(); _multitons.Clear();
} }

@ -35,6 +35,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Factories\" /> <Folder Include="Factories\" />
<Folder Include="Interfaces\Factories\" /> <Folder Include="Interfaces\Factories\" />
<Folder Include="Validation" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -274,7 +274,7 @@
</summary> </summary>
<param name="message">The exception message</param> <param name="message">The exception message</param>
</member> </member>
<member name="T:LightweightIocContainer.Factories.CustomTypedFactory"> <member name="T:LightweightIocContainer.Factories.CustomTypedFactory`1">
<summary> <summary>
<see cref="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory"/> implementation for custom implemented factories <see cref="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory"/> implementation for custom implemented factories
</summary> </summary>
@ -303,6 +303,16 @@
<exception cref="T:LightweightIocContainer.Exceptions.InvalidFactoryRegistrationException">Factory registration is invalid</exception> <exception cref="T:LightweightIocContainer.Exceptions.InvalidFactoryRegistrationException">Factory registration is invalid</exception>
<exception cref="T:LightweightIocContainer.Exceptions.IllegalAbstractMethodCreationException">Creation of abstract methods are illegal in their current state</exception> <exception cref="T:LightweightIocContainer.Exceptions.IllegalAbstractMethodCreationException">Creation of abstract methods are illegal in their current state</exception>
</member> </member>
<member name="T:LightweightIocContainer.Factories.TypedFactoryBase`1">
<summary>
Base class for the <see cref="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory"/>
</summary>
</member>
<member name="P:LightweightIocContainer.Factories.TypedFactoryBase`1.CreateMethods">
<summary>
The create methods of this <see cref="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory"/>
</summary>
</member>
<member name="T:LightweightIocContainer.GenericMethodCaller"> <member name="T:LightweightIocContainer.GenericMethodCaller">
<summary> <summary>
Helper class to call a generic method without generic type parameters Helper class to call a generic method without generic type parameters
@ -366,6 +376,11 @@
Non-generic <see cref="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory`1"/> Non-generic <see cref="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory`1"/>
</summary> </summary>
</member> </member>
<member name="P:LightweightIocContainer.Interfaces.Factories.ITypedFactory.CreateMethods">
<summary>
The create methods of this <see cref="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory"/>
</summary>
</member>
<member name="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory`1"> <member name="T:LightweightIocContainer.Interfaces.Factories.ITypedFactory`1">
<summary> <summary>
Class to help implement an abstract typed factory Class to help implement an abstract typed factory
@ -1478,5 +1493,29 @@
<param name="type">The given <see cref="T:System.Type"/></param> <param name="type">The given <see cref="T:System.Type"/></param>
<returns>The default value for the given <see cref="T:System.Type"/></returns> <returns>The default value for the given <see cref="T:System.Type"/></returns>
</member> </member>
<member name="T:LightweightIocContainer.Validation.IocValidator">
<summary>
Validator for your <see cref="T:LightweightIocContainer.IocContainer"/> to check if everything can be resolved with your current setup
</summary>
</member>
<member name="M:LightweightIocContainer.Validation.IocValidator.#ctor(LightweightIocContainer.IocContainer)">
<summary>
Validator for your <see cref="T:LightweightIocContainer.IocContainer"/> to check if everything can be resolved with your current setup
</summary>
<param name="iocContainer">The <see cref="T:LightweightIocContainer.IocContainer"/></param>
</member>
<member name="M:LightweightIocContainer.Validation.IocValidator.AddParameter``2(``1)">
<summary>
Add parameters that can't be default for your type to be created successfully
</summary>
<param name="parameter">The new value of the parameter</param>
<typeparam name="TInterface">The <see cref="T:System.Type"/> of your registered interface</typeparam>
<typeparam name="TParameter">The <see cref="T:System.Type"/> of your <paramref name="parameter"></paramref></typeparam>
</member>
<member name="M:LightweightIocContainer.Validation.IocValidator.Validate">
<summary>
Validates your given <see cref="T:LightweightIocContainer.IocContainer"/> and checks if everything can be resolved with the current setup
</summary>
</member>
</members> </members>
</doc> </doc>

@ -0,0 +1,63 @@
// Author: Gockner, Simon
// Created: 2021-12-02
// Copyright(c) 2021 SimonG. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using LightweightIocContainer.Interfaces.Registrations.Fluent;
namespace LightweightIocContainer.Validation
{
/// <summary>
/// Validator for your <see cref="IocContainer"/> to check if everything can be resolved with your current setup
/// </summary>
public class IocValidator
{
private readonly IocContainer _iocContainer;
private readonly List<(Type type, object parameter)> _parameters;
/// <summary>
/// Validator for your <see cref="IocContainer"/> to check if everything can be resolved with your current setup
/// </summary>
/// <param name="iocContainer">The <see cref="IocContainer"/></param>
public IocValidator(IocContainer iocContainer)
{
_iocContainer = iocContainer;
_parameters = new List<(Type, object)>();
}
/// <summary>
/// Add parameters that can't be default for your type to be created successfully
/// </summary>
/// <param name="parameter">The new value of the parameter</param>
/// <typeparam name="TInterface">The <see cref="Type"/> of your registered interface</typeparam>
/// <typeparam name="TParameter">The <see cref="Type"/> of your <paramref name="parameter"></paramref></typeparam>
public void AddParameter<TInterface, TParameter>(TParameter parameter) => _parameters.Add((typeof(TInterface), parameter));
/// <summary>
/// Validates your given <see cref="IocContainer"/> and checks if everything can be resolved with the current setup
/// </summary>
public void Validate()
{
foreach (var registration in _iocContainer.Registrations)
{
if (registration is IWithFactory { Factory: { } } withFactoryRegistration)
{
(from createMethod in withFactoryRegistration.Factory.CreateMethods
select createMethod.GetParameters().Select(p => p.ParameterType)
into parameterTypes
let definedParameters = _parameters.Where(p => p.type == registration.InterfaceType)
select (from parameterType in parameterTypes
let definedParameter = definedParameters
.FirstOrDefault(p => parameterType.IsInstanceOfType(p.parameter))
select definedParameter == default ? parameterType.GetDefault() : definedParameter.parameter).ToArray())
.ToList()
.ForEach(p => _iocContainer.Resolve(registration.InterfaceType, p, null));
}
else
_iocContainer.Resolve(registration.InterfaceType, null, null);
}
}
}
}

@ -0,0 +1,74 @@
// Author: Gockner, Simon
// Created: 2021-12-03
// Copyright(c) 2021 SimonG. All Rights Reserved.
using JetBrains.Annotations;
using LightweightIocContainer;
using LightweightIocContainer.Interfaces;
using LightweightIocContainer.Interfaces.Installers;
using LightweightIocContainer.Validation;
using Moq;
using NUnit.Framework;
namespace Test.LightweightIocContainer
{
[TestFixture]
public class IocValidatorTest
{
public interface ITest
{
}
[UsedImplicitly]
public interface IParameter
{
bool Method();
}
private class Test : ITest
{
public Test(IParameter parameter) => parameter?.Method();
}
[UsedImplicitly]
public interface ITestFactory
{
ITest Create(IParameter parameter);
}
private class TestInstaller : IIocInstaller
{
public void Install(IIocContainer container) => container.Register<ITest, Test>().WithFactory<ITestFactory>();
}
[Test]
public void TestValidate()
{
IocContainer iocContainer = new();
iocContainer.Install(new TestInstaller());
IocValidator validator = new(iocContainer);
validator.Validate();
}
[Test]
public void TestValidateWithParameter()
{
IocContainer iocContainer = new();
iocContainer.Install(new TestInstaller());
IocValidator validator = new(iocContainer);
Mock<IParameter> parameterMock = new();
parameterMock.Setup(p => p.Method()).Returns(true);
validator.AddParameter<ITest, IParameter>(parameterMock.Object);
validator.Validate();
parameterMock.Verify(p => p.Method(), Times.Once);
}
}
}
Loading…
Cancel
Save