diff --git a/Docs/IoC_net40.md b/Docs/IoC_net40.md index df53f62d..8f203a69 100644 --- a/Docs/IoC_net40.md +++ b/Docs/IoC_net40.md @@ -337,6 +337,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8125,6 +8126,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_net40.xml b/Docs/IoC_net40.xml index a94e4f19..12c61e4a 100644 --- a/Docs/IoC_net40.xml +++ b/Docs/IoC_net40.xml @@ -5598,6 +5598,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/Docs/IoC_net48.md b/Docs/IoC_net48.md index df53f62d..8f203a69 100644 --- a/Docs/IoC_net48.md +++ b/Docs/IoC_net48.md @@ -337,6 +337,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8125,6 +8126,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_net48.xml b/Docs/IoC_net48.xml index a94e4f19..12c61e4a 100644 --- a/Docs/IoC_net48.xml +++ b/Docs/IoC_net48.xml @@ -5598,6 +5598,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/Docs/IoC_net5.0.md b/Docs/IoC_net5.0.md index df53f62d..8f203a69 100644 --- a/Docs/IoC_net5.0.md +++ b/Docs/IoC_net5.0.md @@ -337,6 +337,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8125,6 +8126,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_net5.0.xml b/Docs/IoC_net5.0.xml index a94e4f19..12c61e4a 100644 --- a/Docs/IoC_net5.0.xml +++ b/Docs/IoC_net5.0.xml @@ -5598,6 +5598,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/Docs/IoC_netcoreapp1.0.md b/Docs/IoC_netcoreapp1.0.md index 90d62275..12e2a688 100644 --- a/Docs/IoC_netcoreapp1.0.md +++ b/Docs/IoC_netcoreapp1.0.md @@ -335,6 +335,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8089,6 +8090,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_netcoreapp1.0.xml b/Docs/IoC_netcoreapp1.0.xml index 51a1a395..82870155 100644 --- a/Docs/IoC_netcoreapp1.0.xml +++ b/Docs/IoC_netcoreapp1.0.xml @@ -5584,6 +5584,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/Docs/IoC_netcoreapp2.0.md b/Docs/IoC_netcoreapp2.0.md index df53f62d..8f203a69 100644 --- a/Docs/IoC_netcoreapp2.0.md +++ b/Docs/IoC_netcoreapp2.0.md @@ -337,6 +337,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8125,6 +8126,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_netcoreapp2.0.xml b/Docs/IoC_netcoreapp2.0.xml index a94e4f19..12c61e4a 100644 --- a/Docs/IoC_netcoreapp2.0.xml +++ b/Docs/IoC_netcoreapp2.0.xml @@ -5598,6 +5598,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/Docs/IoC_netcoreapp3.1.md b/Docs/IoC_netcoreapp3.1.md index df53f62d..8f203a69 100644 --- a/Docs/IoC_netcoreapp3.1.md +++ b/Docs/IoC_netcoreapp3.1.md @@ -337,6 +337,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8125,6 +8126,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_netcoreapp3.1.xml b/Docs/IoC_netcoreapp3.1.xml index a94e4f19..12c61e4a 100644 --- a/Docs/IoC_netcoreapp3.1.xml +++ b/Docs/IoC_netcoreapp3.1.xml @@ -5598,6 +5598,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/Docs/IoC_netstandard1.0.md b/Docs/IoC_netstandard1.0.md index 90d62275..12e2a688 100644 --- a/Docs/IoC_netstandard1.0.md +++ b/Docs/IoC_netstandard1.0.md @@ -335,6 +335,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8089,6 +8090,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_netstandard1.0.xml b/Docs/IoC_netstandard1.0.xml index 51a1a395..82870155 100644 --- a/Docs/IoC_netstandard1.0.xml +++ b/Docs/IoC_netstandard1.0.xml @@ -5584,6 +5584,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/Docs/IoC_netstandard2.1.md b/Docs/IoC_netstandard2.1.md index df53f62d..8f203a69 100644 --- a/Docs/IoC_netstandard2.1.md +++ b/Docs/IoC_netstandard2.1.md @@ -337,6 +337,7 @@ - [ContainerParameter](#P-IoC-IBuildContext-ContainerParameter 'IoC.IBuildContext.ContainerParameter') - [Depth](#P-IoC-IBuildContext-Depth 'IoC.IBuildContext.Depth') - [Key](#P-IoC-IBuildContext-Key 'IoC.IBuildContext.Key') + - [Parent](#P-IoC-IBuildContext-Parent 'IoC.IBuildContext.Parent') - [AddParameter(parameterExpression)](#M-IoC-IBuildContext-AddParameter-System-Linq-Expressions-ParameterExpression- 'IoC.IBuildContext.AddParameter(System.Linq.Expressions.ParameterExpression)') - [CreateChild(key,container)](#M-IoC-IBuildContext-CreateChild-IoC-Key,IoC-IContainer- 'IoC.IBuildContext.CreateChild(IoC.Key,IoC.IContainer)') - [CreateExpression(defaultExpression)](#M-IoC-IBuildContext-CreateExpression-System-Linq-Expressions-Expression- 'IoC.IBuildContext.CreateExpression(System.Linq.Expressions.Expression)') @@ -8125,6 +8126,13 @@ The depth of current context in the build tree. The target key to build resolver. + +### Parent `property` + +##### Summary + +Provides a parent context or `null`. + ### AddParameter(parameterExpression) `method` diff --git a/Docs/IoC_netstandard2.1.xml b/Docs/IoC_netstandard2.1.xml index a94e4f19..12c61e4a 100644 --- a/Docs/IoC_netstandard2.1.xml +++ b/Docs/IoC_netstandard2.1.xml @@ -5598,6 +5598,11 @@ Represents an abstract build context. + + + Provides a parent context or null. + + The target key to build resolver. diff --git a/IoC.Source/IoC.cs b/IoC.Source/IoC.cs index 27b03131..21d23d77 100644 --- a/IoC.Source/IoC.cs +++ b/IoC.Source/IoC.cs @@ -8131,6 +8131,11 @@ namespace IoC [PublicAPI] public interface IBuildContext { + /// + /// Provides a parent context or null. + /// + [CanBeNull] IBuildContext Parent { get; } + /// /// The target key to build resolver. /// @@ -9353,6 +9358,7 @@ namespace IoC.Features using System; using System.Collections.Generic; using System.Linq.Expressions; + using System.Reflection; using Core; using Lifetimes; using static Core.FluentRegister; @@ -9363,6 +9369,8 @@ namespace IoC.Features [PublicAPI] public sealed class FuncFeature : IConfiguration { + [NotNull] private static readonly MethodInfo ResolveWithTagGenericMethodInfo = ((MethodCallExpression)((Expression>)(() => Resolve(default(IContainer), default(Tag), default(object[])))).Body).Method.GetGenericMethodDefinition(); + /// The default instance. public static readonly IConfiguration Set = new FuncFeature(); @@ -9400,7 +9408,34 @@ public bool TryBuildExpression(IBuildContext buildContext, ILifetime lifetime, o var instanceType = genericTypeArguments[paramsCount]; var key = new Key(instanceType, buildContext.Key.Tag); var context = buildContext.CreateChild(key, buildContext.Container); - var instanceExpression = context.CreateExpression(); + var curContext = buildContext.Parent; + var reentrancy = false; + while (curContext != null) + { + if (curContext.Key.Equals(buildContext.Key)) + { + reentrancy = true; + break; + } + + curContext = curContext.Parent; + } + + Expression instanceExpression; + if (!reentrancy) + { + instanceExpression = context.CreateExpression(); + } + else + { + instanceExpression = Expression.Call( + null, + ResolveWithTagGenericMethodInfo.MakeGenericMethod(instanceType), + buildContext.ContainerParameter, + Expression.Constant(buildContext.Key.Tag), + buildContext.ArgsParameter); + } + var parameters = new ParameterExpression[paramsCount]; var parametersArgs = new Expression[paramsCount]; for (var i = 0; i < paramsCount; i++) @@ -9436,6 +9471,9 @@ public bool TryBuildExpression(IBuildContext buildContext, ILifetime lifetime, o return false; } } + + private static T Resolve([NotNull] IContainer container, object tag, [NotNull][ItemCanBeNull] params object[] args) + => container.GetResolver(new Tag(tag))(container, args); } } @@ -11526,7 +11564,6 @@ internal sealed class BuildContext : IBuildContext [NotNull] private readonly IEnumerable _builders; private readonly IList _parameters = new List(); [NotNull] private readonly ICompiler _compiler; - [CanBeNull] internal readonly BuildContext Parent; [NotNull] private readonly IDictionary _typesMap; internal BuildContext( @@ -11552,6 +11589,8 @@ internal BuildContext( _typesMap = parent == null ? new Dictionary() : new Dictionary(parent._typesMap); } + public IBuildContext Parent { get; private set; } + public Key Key { get; } public IContainer Container { get; } @@ -11673,7 +11712,7 @@ private IBuildContext CreateInternal(Key key, IContainer container, bool forBuil public override string ToString() { var path = new List(); - var context = this; + IBuildContext context = this; while (context != null) { path.Add(context); diff --git a/IoC.Tests/IntegrationTests/ResolveTests.cs b/IoC.Tests/IntegrationTests/ResolveTests.cs index c0c95683..966b55dd 100644 --- a/IoC.Tests/IntegrationTests/ResolveTests.cs +++ b/IoC.Tests/IntegrationTests/ResolveTests.cs @@ -560,6 +560,52 @@ public void ContainerShouldResolveGenericWhenList() } } + [Fact] + public void ContainerShouldResolveWhenFactory() + { + // Given + using var container = Container.Create(); + + // When + using (container + .Bind().To() + .Bind>().To>()) + { + var instance = container.Resolve(); + + // Then + instance.New().ShouldBeOfType(); + } + } + + public interface IFactory + { + T Create(); + } + + public class MyFactory: IFactory + { + private readonly Func _func; + + public MyFactory(Func func) => _func = func; + + public T Create() => _func(); + } + + public interface IMyClass + { + IMyClass New(); + } + + public class MyClass: IMyClass + { + private readonly IFactory _factory; + + public MyClass(IFactory factory) => _factory = factory; + + public IMyClass New() => _factory.Create(); + } + #if !NET40 [Fact] public void ContainerShouldResolveGenericWhenComplex() diff --git a/IoC.Tests/IoC.Tests.csproj b/IoC.Tests/IoC.Tests.csproj index 168761d3..b9a28e7b 100644 --- a/IoC.Tests/IoC.Tests.csproj +++ b/IoC.Tests/IoC.Tests.csproj @@ -21,7 +21,7 @@ - + diff --git a/IoC/Core/BuildContext.cs b/IoC/Core/BuildContext.cs index 4bc447c1..8db1a9dd 100644 --- a/IoC/Core/BuildContext.cs +++ b/IoC/Core/BuildContext.cs @@ -19,7 +19,6 @@ internal sealed class BuildContext : IBuildContext [NotNull] private readonly IEnumerable _builders; private readonly IList _parameters = new List(); [NotNull] private readonly ICompiler _compiler; - [CanBeNull] internal readonly BuildContext Parent; [NotNull] private readonly IDictionary _typesMap; internal BuildContext( @@ -45,6 +44,8 @@ internal BuildContext( _typesMap = parent == null ? new Dictionary() : new Dictionary(parent._typesMap); } + public IBuildContext Parent { get; private set; } + public Key Key { get; } public IContainer Container { get; } @@ -166,7 +167,7 @@ private IBuildContext CreateInternal(Key key, IContainer container, bool forBuil public override string ToString() { var path = new List(); - var context = this; + IBuildContext context = this; while (context != null) { path.Add(context); diff --git a/IoC/Features/FuncFeature.cs b/IoC/Features/FuncFeature.cs index 6d7f9dad..61a045fa 100644 --- a/IoC/Features/FuncFeature.cs +++ b/IoC/Features/FuncFeature.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; + using System.Reflection; using Core; using Lifetimes; using static Core.FluentRegister; @@ -13,6 +14,8 @@ [PublicAPI] public sealed class FuncFeature : IConfiguration { + [NotNull] private static readonly MethodInfo ResolveWithTagGenericMethodInfo = ((MethodCallExpression)((Expression>)(() => Resolve(default(IContainer), default(Tag), default(object[])))).Body).Method.GetGenericMethodDefinition(); + /// The default instance. public static readonly IConfiguration Set = new FuncFeature(); @@ -50,7 +53,34 @@ public bool TryBuildExpression(IBuildContext buildContext, ILifetime lifetime, o var instanceType = genericTypeArguments[paramsCount]; var key = new Key(instanceType, buildContext.Key.Tag); var context = buildContext.CreateChild(key, buildContext.Container); - var instanceExpression = context.CreateExpression(); + var curContext = buildContext.Parent; + var reentrancy = false; + while (curContext != null) + { + if (curContext.Key.Equals(buildContext.Key)) + { + reentrancy = true; + break; + } + + curContext = curContext.Parent; + } + + Expression instanceExpression; + if (!reentrancy) + { + instanceExpression = context.CreateExpression(); + } + else + { + instanceExpression = Expression.Call( + null, + ResolveWithTagGenericMethodInfo.MakeGenericMethod(instanceType), + buildContext.ContainerParameter, + Expression.Constant(buildContext.Key.Tag), + buildContext.ArgsParameter); + } + var parameters = new ParameterExpression[paramsCount]; var parametersArgs = new Expression[paramsCount]; for (var i = 0; i < paramsCount; i++) @@ -86,5 +116,8 @@ public bool TryBuildExpression(IBuildContext buildContext, ILifetime lifetime, o return false; } } + + private static T Resolve([NotNull] IContainer container, object tag, [NotNull][ItemCanBeNull] params object[] args) + => container.GetResolver(new Tag(tag))(container, args); } } diff --git a/IoC/IBuildContext.cs b/IoC/IBuildContext.cs index f3236c82..241c32b8 100644 --- a/IoC/IBuildContext.cs +++ b/IoC/IBuildContext.cs @@ -9,6 +9,11 @@ [PublicAPI] public interface IBuildContext { + /// + /// Provides a parent context or null. + /// + [CanBeNull] IBuildContext Parent { get; } + /// /// The target key to build resolver. ///