diff --git a/src/Ninject.Extensions.ChildKernel.Test/ChildKernelTest.cs b/src/Ninject.Extensions.ChildKernel.Test/ChildKernelTest.cs index cd8a972..7f38568 100644 --- a/src/Ninject.Extensions.ChildKernel.Test/ChildKernelTest.cs +++ b/src/Ninject.Extensions.ChildKernel.Test/ChildKernelTest.cs @@ -31,13 +31,66 @@ namespace Ninject.Extensions.ChildKernel using Ninject.Parameters; using Ninject.Planning.Bindings; using Ninject.Planning.Bindings.Resolvers; + using Ninject.Selection; + using Ninject.Syntax; using Xunit; - + + public class ChildKernelTestWithIResolutionRoot : BaseChildKernelTest + { + /// + /// Initializes a new instance of the class. + /// + public ChildKernelTestWithIResolutionRoot() + { + this.parentKernel = new StandardKernel(); + this.testee = new ChildKernel((IResolutionRoot)this.parentKernel); + } + } + + public class ChildKernelTestWithParentAndSettings : BaseChildKernelTest + { + /// + /// Initializes a new instance of the class. + /// + public ChildKernelTestWithParentAndSettings() + { + this.parentKernel = new StandardKernel(); + this.testee = new ChildKernel(this.parentKernel, this.parentKernel.Settings); + } + } + + public class ChildKernelTestWithComponents : BaseChildKernelTest + { + /// + /// Initializes a new instance of the class. + /// + public ChildKernelTestWithComponents() + { + this.parentKernel = new StandardKernel(); + this.testee = new ChildKernel( + this.parentKernel, + this.parentKernel.Settings, + this.parentKernel.Components); + } + } + + public class ChildKernelTestWithOnlyKernel : BaseChildKernelTest + { + /// + /// Initializes a new instance of the class. + /// + public ChildKernelTestWithOnlyKernel() + { + this.parentKernel = new StandardKernel(); + this.testee = new ChildKernel(this.parentKernel); + } + } + /// /// Tests the implementation of . /// - public class ChildKernelTest + public abstract class BaseChildKernelTest { /// /// Name parent's foo. @@ -62,21 +115,12 @@ public class ChildKernelTest /// /// The object under test. /// - private readonly IKernel testee; + protected IKernel testee; /// /// The parent kernel. /// - private readonly IKernel parentKernel; - - /// - /// Initializes a new instance of the class. - /// - public ChildKernelTest() - { - this.parentKernel = new StandardKernel(); - this.testee = new ChildKernel(this.parentKernel); - } + protected IKernel parentKernel; /// /// All known dependencies the are resolved on child kernel. @@ -181,6 +225,47 @@ public void SelectCorrectConstructorWhenBindingsAcrossKernels() baz.Bar.Should().NotBeNull(); } + [Fact] + public void ChildKernelCannotAccessParentKernelComponents() + { + this.testee.Components.Add(); + + var nameOfBarMissingBindingResolver = nameof(BarMissingBindingResolver); + + this.parentKernel.Components + .GetAll() + .Select(i => i.GetType().Name) + .FirstOrDefault(i => i.Equals(nameOfBarMissingBindingResolver, StringComparison.InvariantCultureIgnoreCase)) + .Should().BeNull(); + } + + [Fact] + public void DisposeOfChildKernelDoesNotChangeParentKernelComponents() + { + this.parentKernel.Components.Add(); + this.testee.Dispose(); + + this.parentKernel.Components.Get().Should().NotBeNull(); + } + + [Fact] + public void AddImmutableComponent() + { + Assert.Throws(() => this.testee.Components.Add()); + } + + [Fact] + public void RemoveImmutableComponent() + { + Assert.Throws(() => this.testee.Components.Remove()); + } + + [Fact] + public void RemoveAllImmutableComponents() + { + Assert.Throws(() => this.testee.Components.RemoveAll(typeof(ISelector))); + } + public class BarMissingBindingResolver : NinjectComponent, IMissingBindingResolver { private readonly IKernel kernel; diff --git a/src/Ninject.Extensions.ChildKernel.Test/Ninject.Extensions.ChildKernel.Test.csproj b/src/Ninject.Extensions.ChildKernel.Test/Ninject.Extensions.ChildKernel.Test.csproj index 966b863..1cebe90 100644 --- a/src/Ninject.Extensions.ChildKernel.Test/Ninject.Extensions.ChildKernel.Test.csproj +++ b/src/Ninject.Extensions.ChildKernel.Test/Ninject.Extensions.ChildKernel.Test.csproj @@ -10,7 +10,7 @@ Properties Ninject.Extensions.ChildKernel Ninject.Extensions.ChildKernel.Test - v3.5 + v4.5 512 false ..\Ninject.snk @@ -34,7 +34,8 @@ false false true - Client + + true @@ -61,7 +62,7 @@ False - ..\..\lib\Ninject\net-3.5\Ninject.dll + ..\..\lib\Ninject\net-4.5\Ninject.dll diff --git a/src/Ninject.Extensions.ChildKernel/ChildActivationCache.cs b/src/Ninject.Extensions.ChildKernel/ChildActivationCache.cs index fac66f0..dfca68f 100644 --- a/src/Ninject.Extensions.ChildKernel/ChildActivationCache.cs +++ b/src/Ninject.Extensions.ChildKernel/ChildActivationCache.cs @@ -21,6 +21,7 @@ namespace Ninject.Extensions.ChildKernel { using Ninject.Activation.Caching; using Ninject.Components; + using System; /// /// The activation cache of child kernels. @@ -38,6 +39,11 @@ public class ChildActivationCache : NinjectComponent, IActivationCache /// The kernel. public ChildActivationCache(IKernel kernel) { + if (kernel == null) + { + throw new ArgumentNullException(nameof(kernel)); + } + this.parentCache = ((IChildKernel)kernel).ParentResolutionRoot.Get().Components.Get(); } diff --git a/src/Ninject.Extensions.ChildKernel/ChildKernel.cs b/src/Ninject.Extensions.ChildKernel/ChildKernel.cs index 923c903..82c20f1 100644 --- a/src/Ninject.Extensions.ChildKernel/ChildKernel.cs +++ b/src/Ninject.Extensions.ChildKernel/ChildKernel.cs @@ -19,67 +19,105 @@ namespace Ninject.Extensions.ChildKernel { - using System; using System.Collections.Generic; using Ninject; using Ninject.Activation; using Ninject.Activation.Caching; + using Ninject.Activation.Strategies; + using Ninject.Components; + using Ninject.Injection; using Ninject.Modules; + using Ninject.Planning; + using Ninject.Planning.Bindings.Resolvers; + using Ninject.Planning.Strategies; + using Ninject.Selection; using Ninject.Selection.Heuristics; using Ninject.Syntax; + using System; /// /// This is a kernel with a parent kernel. Any binding that can not be resolved by this kernel is forwarded to the /// parent. /// - public class ChildKernel : StandardKernel, IChildKernel + public class ChildKernel : KernelBase, IChildKernel { /// - /// The parent kernel. + /// Initializes a new instance of the class. /// - private readonly IResolutionRoot parent; + /// The parent. + /// The modules. + public ChildKernel(IResolutionRoot parent, params INinjectModule[] modules) + : base(CreateComponentContainerOfStandartKernel(), new NinjectSettings(), modules) + { + if (parent == null) + { + throw new ArgumentNullException(nameof(parent)); + } + + ParentResolutionRoot = parent; + } /// /// Initializes a new instance of the class. /// /// The parent. + /// The settings. /// The modules. - public ChildKernel(IResolutionRoot parent, params INinjectModule[] modules) - : base(modules) + public ChildKernel(IResolutionRoot parent, INinjectSettings settings, params INinjectModule[] modules) + : base(CreateComponentContainerOfStandartKernel(settings), settings, modules) { - this.parent = parent; + if (parent == null) + { + throw new ArgumentNullException(nameof(parent)); + } - this.Components.RemoveAll(); - this.Components.Add(); - - this.Components.RemoveAll(); - this.Components.Add(); + ParentResolutionRoot = parent; } - + /// /// Initializes a new instance of the class. /// /// The parent. /// The settings. + /// The components. /// The modules. - public ChildKernel(IResolutionRoot parent, INinjectSettings settings, params INinjectModule[] modules) - : base(settings, modules) + public ChildKernel( + IResolutionRoot parent, + INinjectSettings settings, + IComponentContainer components, + params INinjectModule[] modules) + : base(new ChildKernelComponentContainer(components), settings, modules) { - this.parent = parent; + if (parent == null) + { + throw new ArgumentNullException(nameof(parent)); + } + + ParentResolutionRoot = parent; } /// - /// Gets the parent resolution root. + /// Initializes a new instance of the class. /// - /// The parent resolution root. - public IResolutionRoot ParentResolutionRoot + /// The parent. + /// The modules. + public ChildKernel(IKernel parent, params INinjectModule[] modules) + : base(new ChildKernelComponentContainer(parent.Components), parent.Settings, modules) { - get + if (parent == null) { - return this.parent; + throw new ArgumentNullException(nameof(parent)); } + + ParentResolutionRoot = parent; } - + + /// + /// Gets the parent resolution root. + /// + /// The parent resolution root. + public IResolutionRoot ParentResolutionRoot { get; } + /// /// Determines whether the specified request can be resolved. /// @@ -89,7 +127,7 @@ public IResolutionRoot ParentResolutionRoot /// public override bool CanResolve(IRequest request) { - return base.CanResolve(request) || this.parent.CanResolve(request, true); + return base.CanResolve(request) || this.ParentResolutionRoot.CanResolve(request, true); } /// @@ -102,7 +140,7 @@ public override bool CanResolve(IRequest request) /// public override bool CanResolve(IRequest request, bool ignoreImplicitBindings) { - return base.CanResolve(request, ignoreImplicitBindings) || this.parent.CanResolve(request, true); + return base.CanResolve(request, ignoreImplicitBindings) || this.ParentResolutionRoot.CanResolve(request, true); } /// @@ -120,9 +158,9 @@ public override IEnumerable Resolve(IRequest request) return base.Resolve(request); } - if (this.parent.CanResolve(request, true)) + if (this.ParentResolutionRoot.CanResolve(request, true)) { - return this.parent.Resolve(request); + return this.ParentResolutionRoot.Resolve(request); } try @@ -133,7 +171,7 @@ public override IEnumerable Resolve(IRequest request) { try { - return this.parent.Resolve(request); + return this.ParentResolutionRoot.Resolve(request); } catch (ActivationException) { @@ -142,5 +180,60 @@ public override IEnumerable Resolve(IRequest request) throw; } } + + protected override void AddComponents() + { + } + + protected override IKernel KernelInstance => this; + + /// + /// Create container with components of + /// + /// The settings. + private static IComponentContainer CreateComponentContainerOfStandartKernel(INinjectSettings settings = null) + { + var container = new ChildKernelComponentContainer(); + + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + + if (settings != null && !settings.ActivationCacheDisabled) + { + container.Add(); + } + + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + + if (settings != null && !settings.UseReflectionBasedInjection) + { + container.Add(); + } + else + { + container.Add(); + } + + container.Add(); + container.Add(); + container.Add(); + container.Add(); + container.Add(); + + return container; + } } -} \ No newline at end of file +} diff --git a/src/Ninject.Extensions.ChildKernel/ChildKernelComponentContainer.cs b/src/Ninject.Extensions.ChildKernel/ChildKernelComponentContainer.cs new file mode 100644 index 0000000..fa099f7 --- /dev/null +++ b/src/Ninject.Extensions.ChildKernel/ChildKernelComponentContainer.cs @@ -0,0 +1,194 @@ +namespace Ninject.Extensions.ChildKernel +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Ninject.Activation.Caching; + using Ninject.Components; + using Ninject.Infrastructure.Disposal; + using Ninject.Selection; + using Ninject.Selection.Heuristics; + + public class ChildKernelComponentContainer : DisposableObject, IComponentContainer + { + private readonly IComponentContainer _parentComponentContainer; + private readonly IComponentContainer _childComponentContainer; + + private readonly Type[] _immutableComponents; + + /// + /// Initializes a new instance of the class. + /// + /// The parent component container. + public ChildKernelComponentContainer(IComponentContainer parentComponentContainer) + { + if (parentComponentContainer == null) + { + throw new ArgumentNullException(nameof(parentComponentContainer)); + } + + _parentComponentContainer = parentComponentContainer; + _childComponentContainer = new ComponentContainer(); + + _childComponentContainer.Add(); + _childComponentContainer.Add(); + _childComponentContainer.Add(); + _childComponentContainer.Kernel = Kernel; + + _immutableComponents = new[] + { + typeof(IActivationCache), + typeof(IConstructorScorer), + typeof(ISelector) + }; + } + + /// + /// Initializes a new instance of the class. + /// + public ChildKernelComponentContainer(): this(new ComponentContainer()) + { + } + + /// + /// Releases resources held by the object. + /// + /// True if called manually, otherwise by GC. + public override void Dispose(bool disposing) + { + _childComponentContainer.Dispose(); + + base.Dispose(disposing); + } + + /// + /// Registers a component in the container. + /// + /// The component type. + /// The component's implementation type. + public void Add() + where TComponent : INinjectComponent + where TImplementation : TComponent, INinjectComponent + { + CheckImmutableComponent(typeof(TComponent)); + this._childComponentContainer.Add(); + } + + /// + /// Registers a transient component in the container. + /// + /// The component type. + /// The component's implementation type. + public void AddTransient() + where TComponent : INinjectComponent + where TImplementation : TComponent, INinjectComponent + { + this._childComponentContainer.AddTransient(); + } + + /// + /// Removes all registrations for the specified component. + /// + /// The component type. + public void RemoveAll() + where T : INinjectComponent + { + this._childComponentContainer.RemoveAll(); + } + + /// + /// Removes the specified registration. + /// + /// The component type. + /// The implementation type. + public void Remove() + where T : INinjectComponent + where TImplementation : T + { + CheckImmutableComponent(typeof(T)); + this._childComponentContainer.Remove(); + } + + /// + /// Removes all registrations for the specified component. + /// + /// The component type. + public void RemoveAll(Type component) + { + CheckImmutableComponent(component); + this._childComponentContainer.RemoveAll(component); + } + + /// + /// Gets one instance of the specified component. + /// + /// The component type. + /// The instance of the component. + public T Get() + where T : INinjectComponent + { + return (T)this.Get(typeof(T)); + } + + /// + /// Gets all available instances of the specified component. + /// + /// The component type. + /// A series of instances of the specified component. + public IEnumerable GetAll() + where T : INinjectComponent + { + return this.GetAll(typeof(T)).Cast(); + } + + /// + /// Gets one instance of the specified component. + /// + /// The component type. + /// The instance of the component. + public object Get(Type component) + { + try + { + return this._childComponentContainer.Get(component); + } + catch (InvalidOperationException) + { + return this._parentComponentContainer.Get(component); + } + } + + /// + /// Gets all available instances of the specified component. + /// + /// The component type. + /// A series of instances of the specified component. + public IEnumerable GetAll(Type component) + { + return this._childComponentContainer.GetAll(component) + .Concat(this._parentComponentContainer.GetAll(component)); + } + + /// + /// The kernel + /// + public IKernel Kernel { + get + { + return this._childComponentContainer.Kernel; + } + set + { + this._childComponentContainer.Kernel = value; + } + } + + private void CheckImmutableComponent(Type component) + { + if (_immutableComponents.Any(t => t == component)) + { + throw new InvalidOperationException($"Component {nameof(component)} is immutable in childKernel."); + } + } + } +} diff --git a/src/Ninject.Extensions.ChildKernel/Ninject.Extensions.ChildKernel.csproj b/src/Ninject.Extensions.ChildKernel/Ninject.Extensions.ChildKernel.csproj index 5f2edde..68453d1 100644 --- a/src/Ninject.Extensions.ChildKernel/Ninject.Extensions.ChildKernel.csproj +++ b/src/Ninject.Extensions.ChildKernel/Ninject.Extensions.ChildKernel.csproj @@ -1,114 +1,116 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {9831975F-FA35-4B8B-A545-1A06852625BB} - Library - Properties - Ninject.Extensions.ChildKernel - Ninject.Extensions.ChildKernel - v3.5 - 512 - true - ..\Ninject.snk - - - - - 3.5 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - Client - - - true - full - false - ..\..\build\debug\ - DEBUG;TRACE - prompt - 4 - - - AllRules.ruleset - - - pdbonly - true - ..\..\build\release\ - TRACE - prompt - 4 - ..\..\build\release\Ninject.Extensions.ChildKernel.XML - AllRules.ruleset - - - - False - ..\..\lib\Ninject\net-3.5\Ninject.dll - - - - 3.5 - - - 3.5 - - - 3.5 - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {9831975F-FA35-4B8B-A545-1A06852625BB} + Library + Properties + Ninject.Extensions.ChildKernel + Ninject.Extensions.ChildKernel + v4.5 + 512 + true + ..\Ninject.snk + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + ..\..\build\debug\ + DEBUG;TRACE + prompt + 4 + + + AllRules.ruleset + + + pdbonly + true + ..\..\build\release\ + TRACE + prompt + 4 + ..\..\build\release\Ninject.Extensions.ChildKernel.XML + AllRules.ruleset + + + + False + ..\..\lib\Ninject\net-4.5\Ninject.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + Properties\SharedAssemblyInfo.cs + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + \ No newline at end of file diff --git a/src/Ninject.Extensions.ChildKernel/Properties/AssemblyInfo.cs b/src/Ninject.Extensions.ChildKernel/Properties/AssemblyInfo.cs index ad7445d..3ad1319 100644 --- a/src/Ninject.Extensions.ChildKernel/Properties/AssemblyInfo.cs +++ b/src/Ninject.Extensions.ChildKernel/Properties/AssemblyInfo.cs @@ -29,8 +29,5 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("39527a43-4711-438a-b3a8-63dc45fe45ee")] -#if !NO_PARTIAL_TRUST -[assembly: AllowPartiallyTrustedCallers] -#endif [assembly: AssemblyDescriptionAttribute("Ninject extension that allows that child kernels can be defined.")]