- remove IocIgnoreConstructorAttribute

- allow registration of custom ignoreConstructor attribute
ImplementExpressionTrees
Simon G 3 years ago
parent 7f23dc8e81
commit df331dc26d
  1. 16
      LightweightIocContainer/Annotations/IocIgnoreConstructorAttribute.cs
  2. 21
      LightweightIocContainer/Exceptions/InvalidIgnoreConstructorAttributeException.cs
  3. 8
      LightweightIocContainer/Interfaces/IIocContainer.cs
  4. 28
      LightweightIocContainer/IocContainer.cs
  5. 36
      LightweightIocContainer/LightweightIocContainer.xml
  6. 10
      Test.LightweightIocContainer.Validation/IocValidatorTest.cs
  7. 82
      Test.LightweightIocContainer/IgnoreConstructorAttributeTest.cs

@ -1,16 +0,0 @@
// Author: Gockner, Simon
// Created: 2022-09-01
// Copyright(c) 2022 SimonG. All Rights Reserved.
using LightweightIocContainer.Interfaces;
namespace LightweightIocContainer.Annotations;
/// <summary>
/// If a constructor is annotated with this attribute it will be ignored by the <see cref="IIocContainer"/>
/// </summary>
[AttributeUsage(AttributeTargets.Constructor)]
public class IocIgnoreConstructorAttribute : Attribute
{
}

@ -0,0 +1,21 @@
// Author: Gockner, Simon
// Created: 2022-09-06
// Copyright(c) 2022 SimonG. All Rights Reserved.
namespace LightweightIocContainer.Exceptions;
/// <summary>
/// The passed <see cref="Attribute"/> can't be used on a constructor
/// </summary>
/// <typeparam name="T">The used <see cref="Attribute"/></typeparam>
public class InvalidIgnoreConstructorAttributeException<T> : IocContainerException
{
/// <summary>
/// The passed <see cref="Attribute"/> can't be used on a constructor
/// </summary>
public InvalidIgnoreConstructorAttributeException()
: base($"The passed Attribute ({typeof(T)}) can't be used on a constructor.")
{
}
}

@ -2,6 +2,7 @@
// Created: 2019-05-20 // Created: 2019-05-20
// Copyright(c) 2019 SimonG. All Rights Reserved. // Copyright(c) 2019 SimonG. All Rights Reserved.
using LightweightIocContainer.Exceptions;
using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Installers;
using LightweightIocContainer.Interfaces.Registrations; using LightweightIocContainer.Interfaces.Registrations;
@ -37,4 +38,11 @@ public interface IIocContainer : IDisposable
/// <typeparam name="T">The given <see cref="Type"/></typeparam> /// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <returns>True if the given <see cref="Type"/> is registered with this <see cref="IIocContainer"/>, false if not</returns> /// <returns>True if the given <see cref="Type"/> is registered with this <see cref="IIocContainer"/>, false if not</returns>
bool IsTypeRegistered<T>(); bool IsTypeRegistered<T>();
/// <summary>
/// Register a custom <see cref="Attribute"/> that can annotate a constructor to be ignored
/// </summary>
/// <typeparam name="T">The custom <see cref="Attribute"/></typeparam>
/// <exception cref="InvalidIgnoreConstructorAttributeException{T}">The passed <see cref="Attribute"/> can't be used on a constructor</exception>
void RegisterIgnoreConstructorAttribute<T>() where T : Attribute;
} }

@ -4,7 +4,6 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LightweightIocContainer.Annotations;
using LightweightIocContainer.Exceptions; using LightweightIocContainer.Exceptions;
using LightweightIocContainer.Interfaces; using LightweightIocContainer.Interfaces;
using LightweightIocContainer.Interfaces.Factories; using LightweightIocContainer.Interfaces.Factories;
@ -26,12 +25,15 @@ public class IocContainer : IIocContainer, IIocResolver
private readonly List<(Type type, object? instance)> _singletons = new(); private readonly List<(Type type, object? instance)> _singletons = new();
private readonly List<(Type type, Type scope, ConditionalWeakTable<object, object?> instances)> _multitons = new(); private readonly List<(Type type, Type scope, ConditionalWeakTable<object, object?> instances)> _multitons = new();
private readonly List<Type> _ignoreConstructorAttributes;
/// <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() public IocContainer()
{ {
_registrationFactory = new RegistrationFactory(this); _registrationFactory = new RegistrationFactory(this);
_ignoreConstructorAttributes = new List<Type>();
Registrations = new List<IRegistration>(); Registrations = new List<IRegistration>();
} }
@ -197,7 +199,7 @@ public class IocContainer : IIocContainer, IIocResolver
/// <param name="type">The registered <see cref="Type"/></param> /// <param name="type">The registered <see cref="Type"/></param>
/// <param name="arguments">The given arguments</param> /// <param name="arguments">The given arguments</param>
/// <param name="resolveStack">The current resolve stack</param> /// <param name="resolveStack">The current resolve stack</param>
/// <param name="isFactoryResolve"></param> /// <param name="isFactoryResolve">True if resolve is called from factory, false (default) if not</param>
/// <returns>An instance of the given registered <see cref="Type"/>, an <see cref="InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="InternalFactoryMethodPlaceholder{T}"/> if a factory method is used to create an instance</returns> /// <returns>An instance of the given registered <see cref="Type"/>, an <see cref="InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="InternalFactoryMethodPlaceholder{T}"/> if a factory method is used to create an instance</returns>
/// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered</exception> /// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered</exception>
/// <exception cref="DirectResolveWithRegisteredFactoryNotAllowed">A direct resolve with a registered factory is not allowed</exception> /// <exception cref="DirectResolveWithRegisteredFactoryNotAllowed">A direct resolve with a registered factory is not allowed</exception>
@ -259,11 +261,11 @@ public class IocContainer : IIocContainer, IIocResolver
/// Resolve the given object instance without generic arguments /// Resolve the given object instance without generic arguments
/// </summary> /// </summary>
/// <param name="type">The <see cref="Type"/> of the returned instance</param> /// <param name="type">The <see cref="Type"/> of the returned instance</param>
/// <param name="resolveObject"></param> /// <param name="resolvedObject">The given resolved object</param>
/// <returns>An instance of the given resolved object</returns> /// <returns>An instance of the given resolved object</returns>
/// <exception cref="InternalResolveException">Resolve returned wrong type</exception> /// <exception cref="InternalResolveException">Resolve returned wrong type</exception>
private object? ResolveInstanceNonGeneric(Type type, object resolveObject) => private object? ResolveInstanceNonGeneric(Type type, object resolvedObject) =>
GenericMethodCaller.CallPrivate(this, nameof(ResolveInstance), type, resolveObject); GenericMethodCaller.CallPrivate(this, nameof(ResolveInstance), type, resolvedObject);
/// <summary> /// <summary>
/// Creates an instance of a given <see cref="Type"/> /// Creates an instance of a given <see cref="Type"/>
@ -581,7 +583,7 @@ public class IocContainer : IIocContainer, IIocResolver
private List<ConstructorInfo> TryGetSortedConstructors(Type type) private List<ConstructorInfo> TryGetSortedConstructors(Type type)
{ {
List<ConstructorInfo> sortedConstructors = type.GetConstructors() List<ConstructorInfo> sortedConstructors = type.GetConstructors()
.Where(c => c.GetCustomAttribute<IocIgnoreConstructorAttribute>() == null) .Where(c => !c.GetCustomAttributes().Any(a => _ignoreConstructorAttributes.Contains(a.GetType())))
.OrderByDescending(c => c.GetParameters().Length) .OrderByDescending(c => c.GetParameters().Length)
.ToList(); .ToList();
@ -651,6 +653,20 @@ public class IocContainer : IIocContainer, IIocResolver
/// <returns>True if the given <see cref="Type"/> is registered with this <see cref="IocContainer"/>, false if not</returns> /// <returns>True if the given <see cref="Type"/> is registered with this <see cref="IocContainer"/>, false if not</returns>
public bool IsTypeRegistered<T>() => FindRegistration<T>() != null; public bool IsTypeRegistered<T>() => FindRegistration<T>() != null;
/// <summary>
/// Register a custom <see cref="Attribute"/> that can annotate a constructor to be ignored
/// </summary>
/// <typeparam name="T">The custom <see cref="Attribute"/></typeparam>
/// <exception cref="InvalidIgnoreConstructorAttributeException{T}">The passed <see cref="Attribute"/> can't be used on a constructor</exception>
public void RegisterIgnoreConstructorAttribute<T>() where T : Attribute
{
AttributeUsageAttribute? attributeUsage = typeof(T).GetCustomAttribute<AttributeUsageAttribute>();
if (attributeUsage == null || !attributeUsage.ValidOn.HasFlag(AttributeTargets.Constructor))
throw new InvalidIgnoreConstructorAttributeException<T>();
_ignoreConstructorAttributes.Add(typeof(T));
}
/// <summary> /// <summary>
/// The <see cref="IDisposable.Dispose"/> method /// The <see cref="IDisposable.Dispose"/> method
/// </summary> /// </summary>

@ -13,11 +13,6 @@
<param name="action">The given <see cref="T:System.Action`1"/> to convert</param> <param name="action">The given <see cref="T:System.Action`1"/> to convert</param>
<returns>An <see cref="T:System.Action`1"/> converted from the given <see cref="T:System.Action`1"/></returns> <returns>An <see cref="T:System.Action`1"/> converted from the given <see cref="T:System.Action`1"/></returns>
</member> </member>
<member name="T:LightweightIocContainer.Annotations.IocIgnoreConstructorAttribute">
<summary>
If a constructor is annotated with this attribute it will be ignored by the <see cref="T:LightweightIocContainer.Interfaces.IIocContainer"/>
</summary>
</member>
<member name="T:LightweightIocContainer.Creator"> <member name="T:LightweightIocContainer.Creator">
<summary> <summary>
Helper for dynamic instance creation Helper for dynamic instance creation
@ -214,6 +209,17 @@
</summary> </summary>
<param name="message">The exception message</param> <param name="message">The exception message</param>
</member> </member>
<member name="T:LightweightIocContainer.Exceptions.InvalidIgnoreConstructorAttributeException`1">
<summary>
The passed <see cref="T:System.Attribute"/> can't be used on a constructor
</summary>
<typeparam name="T">The used <see cref="T:System.Attribute"/></typeparam>
</member>
<member name="M:LightweightIocContainer.Exceptions.InvalidIgnoreConstructorAttributeException`1.#ctor">
<summary>
The passed <see cref="T:System.Attribute"/> can't be used on a constructor
</summary>
</member>
<member name="T:LightweightIocContainer.Exceptions.InvalidRegistrationException"> <member name="T:LightweightIocContainer.Exceptions.InvalidRegistrationException">
<summary> <summary>
The registration is not valid The registration is not valid
@ -526,6 +532,13 @@
<typeparam name="T">The given <see cref="T:System.Type"/></typeparam> <typeparam name="T">The given <see cref="T:System.Type"/></typeparam>
<returns>True if the given <see cref="T:System.Type"/> is registered with this <see cref="T:LightweightIocContainer.Interfaces.IIocContainer"/>, false if not</returns> <returns>True if the given <see cref="T:System.Type"/> is registered with this <see cref="T:LightweightIocContainer.Interfaces.IIocContainer"/>, false if not</returns>
</member> </member>
<member name="M:LightweightIocContainer.Interfaces.IIocContainer.RegisterIgnoreConstructorAttribute``1">
<summary>
Register a custom <see cref="T:System.Attribute"/> that can annotate a constructor to be ignored
</summary>
<typeparam name="T">The custom <see cref="T:System.Attribute"/></typeparam>
<exception cref="T:LightweightIocContainer.Exceptions.InvalidIgnoreConstructorAttributeException`1">The passed <see cref="T:System.Attribute"/> can't be used on a constructor</exception>
</member>
<member name="T:LightweightIocContainer.Interfaces.IIocResolver"> <member name="T:LightweightIocContainer.Interfaces.IIocResolver">
<summary> <summary>
Provides <see cref="M:LightweightIocContainer.Interfaces.IIocResolver.Resolve``1"/> methods Provides <see cref="M:LightweightIocContainer.Interfaces.IIocResolver.Resolve``1"/> methods
@ -1011,7 +1024,7 @@
<param name="type">The registered <see cref="T:System.Type"/></param> <param name="type">The registered <see cref="T:System.Type"/></param>
<param name="arguments">The given arguments</param> <param name="arguments">The given arguments</param>
<param name="resolveStack">The current resolve stack</param> <param name="resolveStack">The current resolve stack</param>
<param name="isFactoryResolve"></param> <param name="isFactoryResolve">True if resolve is called from factory, false (default) if not</param>
<returns>An instance of the given registered <see cref="T:System.Type"/>, an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalFactoryMethodPlaceholder`1"/> if a factory method is used to create an instance</returns> <returns>An instance of the given registered <see cref="T:System.Type"/>, an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="T:LightweightIocContainer.ResolvePlaceholders.InternalFactoryMethodPlaceholder`1"/> if a factory method is used to create an instance</returns>
<exception cref="T:LightweightIocContainer.Exceptions.TypeNotRegisteredException">The given <see cref="T:System.Type"/> is not registered</exception> <exception cref="T:LightweightIocContainer.Exceptions.TypeNotRegisteredException">The given <see cref="T:System.Type"/> is not registered</exception>
<exception cref="T:LightweightIocContainer.Exceptions.DirectResolveWithRegisteredFactoryNotAllowed">A direct resolve with a registered factory is not allowed</exception> <exception cref="T:LightweightIocContainer.Exceptions.DirectResolveWithRegisteredFactoryNotAllowed">A direct resolve with a registered factory is not allowed</exception>
@ -1041,7 +1054,7 @@
Resolve the given object instance without generic arguments Resolve the given object instance without generic arguments
</summary> </summary>
<param name="type">The <see cref="T:System.Type"/> of the returned instance</param> <param name="type">The <see cref="T:System.Type"/> of the returned instance</param>
<param name="resolveObject"></param> <param name="resolvedObject">The given resolved object</param>
<returns>An instance of the given resolved object</returns> <returns>An instance of the given resolved object</returns>
<exception cref="T:LightweightIocContainer.Exceptions.InternalResolveException">Resolve returned wrong type</exception> <exception cref="T:LightweightIocContainer.Exceptions.InternalResolveException">Resolve returned wrong type</exception>
</member> </member>
@ -1186,6 +1199,13 @@
<typeparam name="T">The given <see cref="T:System.Type"/></typeparam> <typeparam name="T">The given <see cref="T:System.Type"/></typeparam>
<returns>True if the given <see cref="T:System.Type"/> is registered with this <see cref="T:LightweightIocContainer.IocContainer"/>, false if not</returns> <returns>True if the given <see cref="T:System.Type"/> is registered with this <see cref="T:LightweightIocContainer.IocContainer"/>, false if not</returns>
</member> </member>
<member name="M:LightweightIocContainer.IocContainer.RegisterIgnoreConstructorAttribute``1">
<summary>
Register a custom <see cref="T:System.Attribute"/> that can annotate a constructor to be ignored
</summary>
<typeparam name="T">The custom <see cref="T:System.Attribute"/></typeparam>
<exception cref="T:LightweightIocContainer.Exceptions.InvalidIgnoreConstructorAttributeException`1">The passed <see cref="T:System.Attribute"/> can't be used on a constructor</exception>
</member>
<member name="M:LightweightIocContainer.IocContainer.Dispose"> <member name="M:LightweightIocContainer.IocContainer.Dispose">
<summary> <summary>
The <see cref="M:System.IDisposable.Dispose"/> method The <see cref="M:System.IDisposable.Dispose"/> method
@ -1528,7 +1548,7 @@
<summary> <summary>
Validate that no registration that isn't derived from <see cref="T:LightweightIocContainer.Interfaces.Registrations.IMultitonRegistration"/> has <see cref="F:LightweightIocContainer.Lifestyle.Multiton"/> Validate that no registration that isn't derived from <see cref="T:LightweightIocContainer.Interfaces.Registrations.IMultitonRegistration"/> has <see cref="F:LightweightIocContainer.Lifestyle.Multiton"/>
</summary> </summary>
<exception cref="T:LightweightIocContainer.Exceptions.InvalidRegistrationException"></exception> <exception cref="T:LightweightIocContainer.Exceptions.InvalidRegistrationException">Can't register a type as Lifestyle.Multiton without a scope (Registration is not of type IMultitonRegistration)</exception>
</member> </member>
<member name="M:LightweightIocContainer.Registrations.RegistrationBase.ValidateFactory"> <member name="M:LightweightIocContainer.Registrations.RegistrationBase.ValidateFactory">
<summary> <summary>

@ -4,7 +4,6 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using LightweightIocContainer; using LightweightIocContainer;
using LightweightIocContainer.Annotations;
using LightweightIocContainer.Exceptions; using LightweightIocContainer.Exceptions;
using LightweightIocContainer.Interfaces.Installers; using LightweightIocContainer.Interfaces.Installers;
using LightweightIocContainer.Interfaces.Registrations; using LightweightIocContainer.Interfaces.Registrations;
@ -33,15 +32,6 @@ public class IocValidatorTest
public Test(IParameter parameter) => parameter.Method(); public Test(IParameter parameter) => parameter.Method();
} }
[UsedImplicitly]
private class TestViewModelIgnoreDesignTimeCtor : ITest
{
public TestViewModelIgnoreDesignTimeCtor(IParameter parameter) => parameter.Method();
[IocIgnoreConstructor]
public TestViewModelIgnoreDesignTimeCtor() => throw new Exception();
}
[UsedImplicitly] [UsedImplicitly]
private class TestViewModelDontIgnoreDesignTimeCtor : ITest private class TestViewModelDontIgnoreDesignTimeCtor : ITest
{ {

@ -0,0 +1,82 @@
// Author: Gockner, Simon
// Created: 2022-09-07
// Copyright(c) 2022 SimonG. All Rights Reserved.
using JetBrains.Annotations;
using LightweightIocContainer;
using LightweightIocContainer.Exceptions;
using NUnit.Framework;
namespace Test.LightweightIocContainer;
public class IgnoreConstructorAttributeTest
{
[AttributeUsage(AttributeTargets.Constructor)]
private class IgnoreAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
private class InvalidIgnoreAttribute : Attribute
{
}
[UsedImplicitly]
public interface ITest
{
}
[UsedImplicitly]
private class TestIgnoreCtor : ITest
{
[UsedImplicitly]
private readonly string _name;
public TestIgnoreCtor(string name) => _name = name;
[Ignore]
public TestIgnoreCtor() => throw new Exception();
}
[UsedImplicitly]
private class TestOnlyIgnoredCtor : ITest
{
[Ignore]
public TestOnlyIgnoredCtor() => throw new Exception();
[Ignore]
public TestOnlyIgnoredCtor(string name) => throw new Exception();
}
[Test]
public void TestRegisterValidIgnoreAttribute()
{
IocContainer container = new();
container.Register(r => r.Add<ITest, TestIgnoreCtor>());
container.RegisterIgnoreConstructorAttribute<IgnoreAttribute>();
ITest test = container.Resolve<ITest>("name");
Assert.IsInstanceOf<TestIgnoreCtor>(test);
}
[Test]
public void TestRegisterInvalidIgnoreAttribute()
{
IocContainer container = new();
Assert.Throws<InvalidIgnoreConstructorAttributeException<InvalidIgnoreAttribute>>(() => container.RegisterIgnoreConstructorAttribute<InvalidIgnoreAttribute>());
}
[Test]
public void TestResolveWithOnlyIgnoredCtors()
{
IocContainer container = new();
container.Register(r => r.Add<ITest, TestOnlyIgnoredCtor>());
container.RegisterIgnoreConstructorAttribute<IgnoreAttribute>();
Assert.Throws<NoPublicConstructorFoundException>(() => container.Resolve<ITest>("name"));
}
}
Loading…
Cancel
Save