Skip to content

Commit

Permalink
#468 Enable recursive build up
Browse files Browse the repository at this point in the history
  • Loading branch information
Nigel Sampson committed Jul 15, 2019
1 parent d36852e commit 819b9e7
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 10 deletions.
142 changes: 141 additions & 1 deletion src/Caliburn.Micro.Core.Tests/SimpleContainerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace Caliburn.Micro.Core.Tests
Expand Down Expand Up @@ -212,4 +213,143 @@ public void Container_ChooseEmptyConstructorWithoutRegisteredParameter()
Assert.Equal(42, inst.Value);
}
}

public class SimpleContainer_Recursive
{
private interface IComponent { }
private interface IDependency1 { }
private interface IDependency2 { }
private interface IEnumerableDependency { }

private class Component : IComponent
{
public IDependency1 Dependency1 { get; set; }
public NonInterfaceDependency NonInterfaceDependency { get; set; }
}

private class Dependency1 : IDependency1
{
public IDependency2 Dependency2 { get; set; }
public IList<IEnumerableDependency> EnumerableDependencies { get; set; }
}

private class Dependency2 : IDependency2 { }

private class EnumerableDependency1 : IEnumerableDependency
{
public IDependency2 Dependency2 { get; set; }
}

private class EnumerableDependency2 : IEnumerableDependency { }

private class NonInterfaceDependency { }

private class SecondDependency1 : Dependency1 { }

private static void RegisterAllComponents(SimpleContainer container)
{
container.RegisterPerRequest(typeof(IComponent), null, typeof(Component));
container.RegisterPerRequest(typeof(IDependency1), null, typeof(Dependency1));
container.RegisterPerRequest(typeof(IDependency2), null, typeof(Dependency2));
container.RegisterPerRequest(typeof(NonInterfaceDependency), null, typeof(NonInterfaceDependency));
container.RegisterPerRequest(typeof(IEnumerableDependency), null, typeof(EnumerableDependency1));
container.RegisterPerRequest(typeof(IEnumerableDependency), null, typeof(EnumerableDependency2));
}

[Fact]
public void Instances_Are_Recursively_Property_Injected_When_Enabled()
{
var container = new SimpleContainer
{
EnablePropertyInjection = true
};

RegisterAllComponents(container);

var instance = (Component)container.GetInstance<IComponent>();

Assert.NotNull(((Dependency1)instance.Dependency1).Dependency2);
}

[Fact]
public void BuildUp_Injects_All_Registered_Dependencies_Non_Recursively()
{
var container = new SimpleContainer();
RegisterAllComponents(container);

var instance = (Component)container.GetInstance<IComponent>();
container.BuildUp(instance);

Assert.Null(((Dependency1)instance.Dependency1).Dependency2);
}

[Fact]
public void BuildUp_Injects_Dependencies_Recursively()
{
var container = new SimpleContainer
{
EnablePropertyInjection = true
};

RegisterAllComponents(container);

var instance = (Component)container.GetInstance<IComponent>();
container.BuildUp(instance);

Assert.NotNull(((Dependency1)instance.Dependency1).Dependency2);
}

[Fact]
public void BuildUp_Injects_Enumerable_Dependencies()
{
var container = new SimpleContainer
{
EnablePropertyInjection = true
};

RegisterAllComponents(container);

var instance = (Component)container.GetInstance<IComponent>();
container.BuildUp(instance);

Assert.Equal(2, (((Dependency1)instance.Dependency1).EnumerableDependencies.Count));
}

[Fact]
public void BuildUp_Injects_Properties_Of_Enumerable_Dependencies()
{
var container = new SimpleContainer
{
EnablePropertyInjection = true
};

RegisterAllComponents(container);

var instance = (Component)container.GetInstance<IComponent>();
container.BuildUp(instance);

Assert.NotNull(((EnumerableDependency1)(((Dependency1)instance.Dependency1).EnumerableDependencies.First())).Dependency2);
}

[Fact]
public void BuildUp_Throws_When_Multiple_Types_Found_For_Component()
{
var container = new SimpleContainer();
RegisterAllComponents(container);
container.RegisterPerRequest(typeof(IDependency1), null, typeof(SecondDependency1));

var instance = (Component)container.GetInstance<IComponent>();

try
{
container.BuildUp(instance);
}
catch
{
return;
}

Assert.NotNull(null);
}
}
}
53 changes: 44 additions & 9 deletions src/Caliburn.Micro.Core/SimpleContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ private SimpleContainer(IEnumerable<ContainerEntry> entries)
this.entries = new List<ContainerEntry>(entries);
}

/// <summary>
/// Whether to enable recursive property injection for all resolutions.
/// </summary>
public bool EnablePropertyInjection { get; set; }

/// <summary>
/// Registers the instance.
/// </summary>
Expand Down Expand Up @@ -98,7 +103,14 @@ public object GetInstance(Type service, string key)
var entry = GetEntry(service, key);
if (entry != null)
{
return entry.Single()(this);
var instance = entry.Single()(this);

if (EnablePropertyInjection && instance != null)
{
BuildUp(instance);
}

return instance;
}

if (service == null)
Expand All @@ -123,6 +135,11 @@ public object GetInstance(Type service, string key)

for (var i = 0; i < array.Length; i++)
{
if (EnablePropertyInjection)
{
BuildUp(instances[i]);
}

array.SetValue(instances[i], i);
}

Expand Down Expand Up @@ -152,7 +169,23 @@ public bool HasHandler(Type service, string key)
public IEnumerable<object> GetAllInstances(Type service, string key = null)
{
var entries = GetEntry(service, key);
return entries != null ? entries.Select(x => x(this)) : new object[0];

if (entries == null)
{
return new object[0];
}

var instances = entries.Select(e => e(this));

foreach(var instance in instances)
{
if (EnablePropertyInjection && instance != null)
{
BuildUp(instance);
}
}

return instances;
}

/// <summary>
Expand All @@ -161,16 +194,18 @@ public IEnumerable<object> GetAllInstances(Type service, string key = null)
/// <param name = "instance">The instance.</param>
public void BuildUp(object instance)
{
var injectables = from property in instance.GetType().GetRuntimeProperties()
where property.CanRead && property.CanWrite && property.PropertyType.GetTypeInfo().IsInterface
select property;
var properties = instance
.GetType()
.GetRuntimeProperties()
.Where(p => p.CanRead && p.CanWrite && p.PropertyType.GetTypeInfo().IsInterface);

foreach (var propertyInfo in injectables)
foreach (var property in properties)
{
var injection = GetAllInstances(propertyInfo.PropertyType).ToArray();
if (injection.Any())
var value = GetInstance(property.PropertyType, null);

if (value != null)
{
propertyInfo.SetValue(instance, injection.First(), null);
property.SetValue(instance, value, null);
}
}
}
Expand Down

0 comments on commit 819b9e7

Please sign in to comment.