Skip to content

Commit

Permalink
fixed: #506
Browse files Browse the repository at this point in the history
  • Loading branch information
dadhi committed Jul 31, 2022
1 parent 9f571b7 commit 065e319
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 36 deletions.
81 changes: 48 additions & 33 deletions src/DryIoc/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5748,47 +5748,62 @@ public static UnknownServiceResolver AutoResolveConcreteTypeRule(Func<Request, b
return factory.GetExpressionOrDefault(request.WithIfUnresolved(IfUnresolved.ReturnDefault)) != null ? factory : null;
};

/// <summary>Rule to automatically resolves non-registered service type which is: nor interface, nor abstract.
/// For constructor selection we are using <see cref="DryIoc.FactoryMethod.ConstructorWithResolvableArguments"/>.
/// The resolution creates transient services.</summary>
// exclude concrete service types which are pre-defined DryIoc wrapper types
private static IEnumerable<Type> GetConcreteServiceType(Type serviceType, object serviceKey) =>
serviceType.IsAbstract || serviceType.IsOpenGeneric() ||
serviceType.IsGenericType && WrappersSupport.Wrappers.GetValueOrDefault(serviceType.GetGenericTypeDefinition()) != null
? null : serviceType.One(); // use concrete service type as implementation type

// exclude concrete service types which are pre-defined DryIoc wrapper types
private static IEnumerable<Type> GetConcreteServiceType(Type serviceType, object serviceKey, Func<Type, object, bool> serviceCondition) =>
serviceType.IsAbstract || serviceType.IsOpenGeneric() ||
!serviceCondition(serviceType, serviceKey) ||
serviceType.IsGenericType && WrappersSupport.Wrappers.GetValueOrDefault(serviceType.GetGenericTypeDefinition()) != null
? null : serviceType.One(); // use concrete service type as implementation type

// by default the condition checks that factory is resolvable down to dependencies
private static Factory GetConcreteTypeFactory(Type implType, IReuse reuse = null, IfUnresolved ifConcreteTypeIsUnresolved = IfUnresolved.Throw)
{
if (ifConcreteTypeIsUnresolved == IfUnresolved.Throw)
return ReflectionFactory.Of(implType, reuse, DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublicWithoutSameTypeParam);
ReflectionFactory factory = null;
factory = ReflectionFactory.Of(implType, reuse,
DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublicWithoutSameTypeParam,
Setup.With(condition: req => factory?.GetExpressionOrDefault(req.WithIfUnresolved(ifConcreteTypeIsUnresolved)) != null));
return factory;
}

/// <summary>Rule to automatically resolves non-registered service type which is: nor interface, nor abstract, nor registered wrapper type.
/// For constructor selection we are using automatic constructor selection.</summary>
/// <param name="condition">(optional) Condition for requested service type and key.</param>
/// <param name="reuse">(optional) Reuse for concrete types.</param>
/// <returns>New rule.</returns>
public static DynamicRegistrationProvider ConcreteTypeDynamicRegistrations(
Func<Type, object, bool> condition = null, IReuse reuse = null) =>
AutoFallbackDynamicRegistrations((serviceType, serviceKey) =>
{
if (serviceType.IsAbstract ||
serviceType.IsOpenGeneric() || // service type in principle should be concrete, so should not be open-generic
condition != null && !condition(serviceType, serviceKey))
return null;

// exclude concrete service types which are pre-defined DryIoc wrapper types
var openGenericServiceType = serviceType.GetGenericDefinitionOrNull();
if (openGenericServiceType != null && WrappersSupport.Wrappers.GetValueOrDefault(openGenericServiceType) != null)
return null;
AutoFallbackDynamicRegistrations(condition == null
? (Func<Type, object, IEnumerable<Type>>)((serviceType, serviceKey) => GetConcreteServiceType(serviceType, serviceKey))
: (Func<Type, object, IEnumerable<Type>>)((serviceType, serviceKey) => GetConcreteServiceType(serviceType, serviceKey, condition)),
implType => GetConcreteTypeFactory(implType, reuse, IfUnresolved.ReturnDefault));

return serviceType.One(); // use concrete service type as implementation type
},
implType =>
{
ReflectionFactory factory = null;

// the condition checks that factory is resolvable
factory = ReflectionFactory.Of(implType, reuse,
DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublicWithoutSameTypeParam,
Setup.With(condition: req => factory?.GetExpressionOrDefault(req.WithIfUnresolved(IfUnresolved.ReturnDefault)) != null));

return factory;
});

/// <summary>Automatically resolves non-registered service type which is: nor interface, nor abstract.
/// The resolution creates Transient services.</summary>
public Rules WithConcreteTypeDynamicRegistrations(
Func<Type, object, bool> condition = null, IReuse reuse = null) =>
/// <summary>Rule to automatically resolves non-registered service type which is: nor interface, nor abstract, nor registered wrapper type.
/// For constructor selection we are using automatic constructor selection.</summary>
public static DynamicRegistrationProvider ConcreteTypeDynamicRegistrations(
IfUnresolved ifConcreteTypeIsUnresolved,
Func<Type, object, bool> serviceCondition = null, IReuse reuse = null) =>
AutoFallbackDynamicRegistrations(serviceCondition == null
? (Func<Type, object, IEnumerable<Type>>)((serviceType, serviceKey) => GetConcreteServiceType(serviceType, serviceKey))
: (Func<Type, object, IEnumerable<Type>>)((serviceType, serviceKey) => GetConcreteServiceType(serviceType, serviceKey, serviceCondition)),
implType => GetConcreteTypeFactory(implType, reuse, ifConcreteTypeIsUnresolved));

/// <summary>Automatically resolves non-registered service type which is: nor interface, nor abstract.</summary>
public Rules WithConcreteTypeDynamicRegistrations(Func<Type, object, bool> condition = null, IReuse reuse = null) =>
WithDynamicRegistrationsAsFallback(ConcreteTypeDynamicRegistrations(condition, reuse));

/// Replaced with `WithConcreteTypeDynamicRegistrations`
/// <summary>Automatically resolves non-registered service type which is: nor interface, nor abstract.</summary>
public Rules WithConcreteTypeDynamicRegistrations(IfUnresolved ifConcreteTypeIsUnresolved, Func<Type, object, bool> condition = null, IReuse reuse = null) =>
WithDynamicRegistrationsAsFallback(ConcreteTypeDynamicRegistrations(ifConcreteTypeIsUnresolved, condition, reuse));

/// [Obsolete("Replaced with `WithConcreteTypeDynamicRegistrations`")]
public Rules WithAutoConcreteTypeResolution(Func<Request, bool> condition = null)
{
var newRules = Clone();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using NUnit.Framework;

namespace DryIoc.IssuesTests
{
[TestFixture]
public class GHIssue506_WithConcreteTypeDynamicRegistrations_hides_failed_dependency_resolution : ITest
{
public int Run()
{
Test();
return 1;
}

[Test]
public void Test()
{
var containerWithNiceException = new Container();
containerWithNiceException.Register<Service>();

const string expected = @"as parameter ""dependency""";

var ex = Assert.Throws<ContainerException>(() => containerWithNiceException.Resolve<Service>());
StringAssert.Contains(expected, ex.Message);

var containerWithAutoConcreteTypes = new Container(rules => rules.WithConcreteTypeDynamicRegistrations(IfUnresolved.Throw, reuse: Reuse.Transient));

var ex2 = Assert.Throws<ContainerException>(() => containerWithAutoConcreteTypes.Resolve<Service>());
StringAssert.Contains(expected, ex2.Message);
}

internal class Service
{
public Service(IDependency dependency)
{
}
}

internal interface IDependency
{
}
}
}
5 changes: 2 additions & 3 deletions test/DryIoc.TestRunner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ public class Program
public static void Main()
{
RunAllTests();

// new GHIssue470_Regression_v5_when_resolving_Func_of_IEnumerable_of_IService_with_Parameter().Run();

// ObjectLayoutInspector.TypeLayout.PrintLayout<Request>();
// new GHIssue506_WithConcreteTypeDynamicRegistrations_hides_failed_dependency_resolution().Run();
}

public static void RunAllTests()
Expand Down Expand Up @@ -57,6 +55,7 @@ void Run(Func<int> run, string name = null)
new GHIssue460_Getting_instance_from_parent_scope_even_if_replaced_by_Use(),
new Issue548_After_registering_a_factory_Func_is_returned_instead_of_the_result_of_Func(),
new GHIssue470_Regression_v5_when_resolving_Func_of_IEnumerable_of_IService_with_Parameter(),
new GHIssue506_WithConcreteTypeDynamicRegistrations_hides_failed_dependency_resolution(),
new GHIssue471_Regression_v5_using_Rules_SelectKeyedOverDefaultFactory(),
};

Expand Down

0 comments on commit 065e319

Please sign in to comment.