Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Commit

Permalink
initial stab at #42 - I think it's going to work well, eventually ;)
Browse files Browse the repository at this point in the history
New test added to this which currently fails DELIBERATELY.
  • Loading branch information
LordZoltan committed May 14, 2017
1 parent 1729a9c commit 390a531
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 10 deletions.
28 changes: 28 additions & 0 deletions src/Rezolver/Behaviours/OverridingEnumerableBehaviour.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Rezolver.Behaviours
{
/// <summary>
/// This extends the behaviour supplied by the AutoEnumerableBehaviour to the <see cref="OverridingContainer"/>
/// class by chaining the enumerable from the overridden container to the enumerable produced by the overriding container.
/// </summary>
/// <remarks>
/// Note that this class is not an <see cref="ITargetContainerBehaviour"/> like the <see cref="AutoEnumerableBehaviour"/>.
///
/// It is an <see cref="IContainerBehaviour"/> because it only applies to instances of <see cref="OverridingContainer"/>.</remarks>
public class OverridingEnumerableBehaviour : IContainerBehaviour
{
public static OverridingEnumerableBehaviour Instance { get; } = new OverridingEnumerableBehaviour();

private OverridingEnumerableBehaviour() { }
public void Attach(IContainer container, ITargetContainer targets)
{
if (container == null) throw new ArgumentNullException(nameof(container));
if (targets == null) throw new ArgumentNullException(nameof(targets));
targets.RegisterContainer(typeof(IEnumerable<>),
new ConcatenatingEnumerableContainer(container, targets));
}
}
}
61 changes: 58 additions & 3 deletions src/Rezolver/EnumerableTargetContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@

namespace Rezolver
{
using CompiledListFactory = Func<IEnumerable<ITarget>, bool, ITarget>;
using System.Collections;
using CompiledListFactory = Func<IEnumerable<ITarget>, bool, ITarget>;

internal class EnumerableTargetContainer : GenericTargetContainer
internal class EnumerableTargetContainer : GenericTargetContainer
{
#region CompiledListTarget
/// <summary>
Expand Down Expand Up @@ -50,7 +51,7 @@ public object GetObject(IResolveContext context)
private static readonly ConcurrentDictionary<Type, Lazy<CompiledListFactory>> _compiledTargetListFactories
= new ConcurrentDictionary<Type, Lazy<CompiledListFactory>>();

internal static ITarget CreateListTarget(Type elementType, IEnumerable<ITarget> targets, bool asArray = false)
protected virtual ITarget CreateListTarget(Type elementType, IEnumerable<ITarget> targets, bool asArray = false)
{
if (!targets.Any() || !targets.All(t => t is ICompiledTarget))
return new ListTarget(elementType, targets, asArray);
Expand Down Expand Up @@ -112,4 +113,58 @@ public override ITargetContainer CombineWith(ITargetContainer existing, Type typ
return base.CombineWith(existing, type);
}
}

internal class ConcatenatingEnumerableContainer : EnumerableTargetContainer
{
private class EnumerableConcatenator<T> : IEnumerable<T>
{
private IEnumerable<T> _concatenated;

public EnumerableConcatenator(IResolveContext context, IEnumerable<T> baseEnumerable, IEnumerable<T> extra)
{
_concatenated = baseEnumerable.Concat(extra);
}

public IEnumerator<T> GetEnumerator()
{
return _concatenated.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

private readonly OverridingContainer _owner;
private readonly ITargetContainer _targets;

public ConcatenatingEnumerableContainer(IContainer owner, ITargetContainer targets)
: base(targets)
{
if (owner == null) throw new ArgumentNullException(nameof(owner));

_owner = owner as OverridingContainer ?? throw new ArgumentException("owner must be an instance of OverridingContainer", nameof(owner));
_targets = targets ?? throw new ArgumentNullException(nameof(targets));
}

public override ITarget Fetch(Type type)
{
var baseCompiled = _owner.Inner.FetchCompiled(new ResolveContext(_owner.Inner, type));
var overrideTarget = base.Fetch(type);

// we know from above that if type is not IEnumerable<T>, then an exception will occur.
// so this type wrangling is safe

return Target.ForType(typeof(EnumerableConcatenator<>).MakeGenericType(TypeHelpers.GetGenericArguments(type)[0]),
new { context = Target.Resolved<IResolveContext>(), baseEnumerable = baseCompiled.SourceTarget, extra = overrideTarget });
}

public override ITargetContainer CombineWith(ITargetContainer existing, Type type)
{
if (existing is ConcatenatingEnumerableContainer) return existing;

return this;
}
}
}
2 changes: 1 addition & 1 deletion src/Rezolver/GlobalBehaviours.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static GlobalBehaviours()
ContainerBehaviour.UseExpressionCompiler()
.UseMemberBindingBehaviour(MemberBindingBehaviour.BindNone);

// no additional behaviours are currently configured for OverridingContainer
OverridingContainerBehaviour.Add(OverridingEnumerableBehaviour.Instance);
}
}
}
12 changes: 7 additions & 5 deletions src/Rezolver/OverridingContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace Rezolver
/// </remarks>
public sealed class OverridingContainer : Container
{
private readonly IContainer _inner;
public IContainer Inner { get; }

/// <summary>
/// Creates a new instance of the <see cref="OverridingContainer"/>
Expand All @@ -51,9 +51,11 @@ public OverridingContainer(IContainer inner, ITargetContainer targets = null, IC
: base(targets)
{
inner.MustNotBeNull("inner");
_inner = inner;
}
Inner = inner;

(behaviour ?? GlobalBehaviours.OverridingContainerBehaviour).Attach(this, Targets);
}

/// <summary>
/// Called to determine if this container is able to resolve the type specified in the passed <paramref name="context"/>.
/// </summary>
Expand All @@ -62,7 +64,7 @@ public OverridingContainer(IContainer inner, ITargetContainer targets = null, IC
/// <see cref="IResolveContext.RequestedType"/>; otherwise <c>false</c></returns>
public override bool CanResolve(IResolveContext context)
{
return base.CanResolve(context) || _inner.CanResolve(context);
return base.CanResolve(context) || Inner.CanResolve(context);
}

/// <summary>
Expand All @@ -73,7 +75,7 @@ public override bool CanResolve(IResolveContext context)
/// <returns></returns>
protected override ICompiledTarget GetFallbackCompiledRezolveTarget(IResolveContext context)
{
return _inner.FetchCompiled(context);
return Inner.FetchCompiled(context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ protected virtual IScopedContainer CreateScopedContainer(ITargetContainer target
/// <param name="baseContainer">The base container.</param>
/// <param name="newTargets">The new targets.</param>
/// <param name="testName">Name of the test.</param>
protected virtual IContainer CreateOverridingContainer(IContainer baseContainer, ITargetContainer newTargets = null, [CallerMemberName]string testName = null)
protected virtual OverridingContainer CreateOverridingContainer(IContainer baseContainer, ITargetContainer newTargets = null, [CallerMemberName]string testName = null)
{
return new OverridingContainer(baseContainer, newTargets);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,21 @@ public void OverridingContainer_ShouldFallBackToEnumerableTargetInParent()

Assert.Equal(new[] { 1 }, result);
}

[Fact]
public void OverridingContainer_ShouldConcatenateBothEnumerables()
{
var targets = CreateTargetContainer();
targets.RegisterObject(1);
var baseContainer = CreateContainer(targets);

var targets2 = CreateTargetContainer();
targets2.RegisterObject(2);

var container = CreateOverridingContainer(baseContainer, targets2);

var result = container.Resolve<IEnumerable<int>>();
Assert.Equal(new[] { 1, 2 }, result);
}
}
}

0 comments on commit 390a531

Please sign in to comment.