From 740e661cc23f89bf9d102cc4361634774cf72416 Mon Sep 17 00:00:00 2001 From: Simon G Date: Fri, 3 Dec 2021 16:44:57 +0100 Subject: [PATCH] #44: add validator to validate current setup of container --- LightweightIocContainer/IocContainer.cs | 24 +++--- .../LightweightIocContainer.csproj | 1 + .../LightweightIocContainer.xml | 41 +++++++++- .../Validation/IocValidator.cs | 63 ++++++++++++++++ .../IocValidatorTest.cs | 74 +++++++++++++++++++ 5 files changed, 192 insertions(+), 11 deletions(-) create mode 100644 LightweightIocContainer/Validation/IocValidator.cs create mode 100644 Test.LightweightIocContainer/IocValidatorTest.cs diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 762c93e..4c47991 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -25,15 +25,19 @@ namespace LightweightIocContainer { private readonly RegistrationFactory _registrationFactory; - private readonly List _registrations = new List(); private readonly List<(Type type, object instance)> _singletons = new List<(Type, object)>(); private readonly List<(Type type, Type scope, ConditionalWeakTable instances)> _multitons = new List<(Type, Type, ConditionalWeakTable)>(); - /// /// The main container that carries all the s and can resolve all the types you'll ever want /// - public IocContainer() => _registrationFactory = new RegistrationFactory(this); + public IocContainer() + { + _registrationFactory = new RegistrationFactory(this); + Registrations = new List(); + } + + internal List Registrations { get; } /// /// Install the given installers for the current @@ -221,14 +225,14 @@ namespace LightweightIocContainer private void Register(IRegistration registration) { //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); //don't allow lifestyle.multiton without 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)."); - _registrations.Add(registration); + Registrations.Add(registration); } /// @@ -268,7 +272,7 @@ namespace LightweightIocContainer /// The current resolve stack /// An instance of the given /// Could not find function - private object Resolve(Type type, object[] arguments, List resolveStack) => + internal object Resolve(Type type, object[] arguments, List resolveStack) => GenericMethodCaller.Call(this, nameof(ResolveInternal), type, BindingFlags.NonPublic | BindingFlags.Instance, arguments, resolveStack); /// @@ -569,11 +573,11 @@ namespace LightweightIocContainer [CanBeNull] private IRegistration FindRegistration() { - IRegistration registration = _registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); + IRegistration registration = Registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); if (registration != null) return registration; - registration = _registrations.OfType().FirstOrDefault(r => r.ImplementationType == typeof(T)); + registration = Registrations.OfType().FirstOrDefault(r => r.ImplementationType == typeof(T)); if (registration != null) return registration; @@ -581,7 +585,7 @@ namespace LightweightIocContainer if (!typeof(T).GenericTypeArguments.Any()) return null; - List openGenericRegistrations = _registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList(); + List openGenericRegistrations = Registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList(); if (!openGenericRegistrations.Any()) return null; @@ -619,7 +623,7 @@ namespace LightweightIocContainer /// public void Dispose() { - _registrations.Clear(); + Registrations.Clear(); _singletons.Clear(); _multitons.Clear(); } diff --git a/LightweightIocContainer/LightweightIocContainer.csproj b/LightweightIocContainer/LightweightIocContainer.csproj index c5ac3c9..cacdc6c 100644 --- a/LightweightIocContainer/LightweightIocContainer.csproj +++ b/LightweightIocContainer/LightweightIocContainer.csproj @@ -35,6 +35,7 @@ + diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 7d0c8ef..8e891cd 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -274,7 +274,7 @@ The exception message - + implementation for custom implemented factories @@ -303,6 +303,16 @@ Factory registration is invalid Creation of abstract methods are illegal in their current state + + + Base class for the + + + + + The create methods of this + + Helper class to call a generic method without generic type parameters @@ -366,6 +376,11 @@ Non-generic + + + The create methods of this + + Class to help implement an abstract typed factory @@ -1478,5 +1493,29 @@ The given The default value for the given + + + Validator for your to check if everything can be resolved with your current setup + + + + + Validator for your to check if everything can be resolved with your current setup + + The + + + + Add parameters that can't be default for your type to be created successfully + + The new value of the parameter + The of your registered interface + The of your + + + + Validates your given and checks if everything can be resolved with the current setup + + diff --git a/LightweightIocContainer/Validation/IocValidator.cs b/LightweightIocContainer/Validation/IocValidator.cs new file mode 100644 index 0000000..9038750 --- /dev/null +++ b/LightweightIocContainer/Validation/IocValidator.cs @@ -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 +{ + /// + /// Validator for your to check if everything can be resolved with your current setup + /// + public class IocValidator + { + private readonly IocContainer _iocContainer; + private readonly List<(Type type, object parameter)> _parameters; + + /// + /// Validator for your to check if everything can be resolved with your current setup + /// + /// The + public IocValidator(IocContainer iocContainer) + { + _iocContainer = iocContainer; + _parameters = new List<(Type, object)>(); + } + + /// + /// Add parameters that can't be default for your type to be created successfully + /// + /// The new value of the parameter + /// The of your registered interface + /// The of your + public void AddParameter(TParameter parameter) => _parameters.Add((typeof(TInterface), parameter)); + + /// + /// Validates your given and checks if everything can be resolved with the current setup + /// + 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); + } + } + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer/IocValidatorTest.cs b/Test.LightweightIocContainer/IocValidatorTest.cs new file mode 100644 index 0000000..ae26aea --- /dev/null +++ b/Test.LightweightIocContainer/IocValidatorTest.cs @@ -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().WithFactory(); + } + + [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 parameterMock = new(); + parameterMock.Setup(p => p.Method()).Returns(true); + + validator.AddParameter(parameterMock.Object); + + validator.Validate(); + + parameterMock.Verify(p => p.Method(), Times.Once); + } + } +} \ No newline at end of file