Skip to content

Commit

Permalink
Refactor, added constructor selection ability #13
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Csajtai committed May 9, 2017
1 parent a0be113 commit a0ec1ee
Show file tree
Hide file tree
Showing 18 changed files with 334 additions and 143 deletions.
4 changes: 2 additions & 2 deletions src/stashbox.tests/BuildExtensionManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void BuildExtensionManagerTests_AddRegistrationBuildExtension()
using (var container = new StashboxContainer())
{
container.RegisterExtension(post.Object);
container.RegisterInstance(new object());
container.RegisterInstanceAs(new object());
post.Verify(p => p.Initialize(container.ContainerContext));
post.Verify(p => p.OnRegistration(container.ContainerContext,
It.IsAny<Type>(), It.IsAny<Type>(), null));
Expand All @@ -80,7 +80,7 @@ public void BuildExtensionManagerTests_CreateCopy()

using (var child = container.CreateChildContainer())
{
child.RegisterInstance(new object());
child.RegisterInstanceAs(new object());

post2.Verify(p => p.Initialize(child.ContainerContext));
post2.Verify(p => p.OnRegistration(child.ContainerContext,
Expand Down
2 changes: 1 addition & 1 deletion src/stashbox.tests/BuildUpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void BuildUpTests_BuildUp()
container.RegisterType<ITest, Test>();

var test1 = new Test1();
container.WireUp<ITest1>(test1);
container.WireUpAs<ITest1>(test1);

var test2 = new Test2();
var inst = container.BuildUp(test2);
Expand Down
99 changes: 99 additions & 0 deletions src/stashbox.tests/ConstructorSelectionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Stashbox.Exceptions;

namespace Stashbox.Tests
{
[TestClass]
public class ConstructorSelectionTests
{
[TestMethod]
public void ConstructorSelectionTests_ArgTypes()
{
using (var container = new StashboxContainer(config => config.WithUnknownTypeResolution()))
{
container.RegisterType<Test>(context => context.WithConstructorByArgumentTypes(typeof(Dep), typeof(Dep2)));
container.Resolve<Test>();
}
}

[TestMethod]
public void ConstructorSelectionTests_Args()
{
using (var container = new StashboxContainer(config => config.WithUnknownTypeResolution()))
{
var dep = new Dep();
var dep2 = new Dep2();

container.RegisterType<Test>(context => context.WithConstructorByArguments(dep, dep2));
var test = container.Resolve<Test>();

Assert.AreSame(dep, test.Dep);
Assert.AreSame(dep2, test.Dep2);
}
}

[TestMethod]
[ExpectedException(typeof(ResolutionFailedException))]
public void ConstructorSelectionTests_ArgTypes_Throws_ResolutionFailed()
{
using (var container = new StashboxContainer())
{
container.RegisterType<Test>(context => context.WithConstructorByArgumentTypes(typeof(Dep), typeof(Dep2)));
container.Resolve<Test>();
}
}

[TestMethod]
[ExpectedException(typeof(ConstructorNotFoundException))]
public void ConstructorSelectionTests_ArgTypes_Throws_MissingConstructor()
{
using (var container = new StashboxContainer())
{
container.RegisterType<Test>(context => context.WithConstructorByArgumentTypes());
container.Resolve<Test>();
}
}

[TestMethod]
[ExpectedException(typeof(ConstructorNotFoundException))]
public void ConstructorSelectionTests_Args_Throws_MissingConstructor()
{
using (var container = new StashboxContainer())
{
container.RegisterType<Test>(context => context.WithConstructorByArguments());
container.Resolve<Test>();
}
}

public class Dep
{ }

public class Dep2
{ }

public class Dep3
{ }

public class Test
{
public Dep Dep { get; }
public Dep2 Dep2 { get; }

public Test(Dep dep)
{
Assert.Fail("wrong constructor");
}

public Test(Dep dep, Dep2 dep2)
{
this.Dep = dep;
this.Dep2 = dep2;
}

public Test(Dep dep, Dep2 dep2, Dep3 dep3)
{
Assert.Fail("wrong constructor");
}
}
}
}
14 changes: 7 additions & 7 deletions src/stashbox.tests/DisposeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void DisposeTests_Instance()
{
container.RegisterType<ITest2, Test2>();
container.RegisterType<Test3>();
container.RegisterInstance<ITest1>(test);
container.RegisterInstanceAs(test);
test2 = container.Resolve<ITest2>();
test3 = container.Resolve<Test3>();
}
Expand All @@ -81,7 +81,7 @@ public void DisposeTests_Instance_WithoutDisposal()
{
container.RegisterType<ITest2, Test2>();
container.RegisterType<Test3>();
container.RegisterInstance(test, withoutDisposalTracking: true);
container.RegisterInstanceAs(test, withoutDisposalTracking: true);
test2 = container.Resolve<ITest2>();
test3 = container.Resolve<Test3>();
}
Expand Down Expand Up @@ -141,7 +141,7 @@ public void DisposeTests_WireUp()
{
container.RegisterType<ITest2, Test2>();
container.RegisterType<Test3>();
container.WireUp(test);
container.WireUpAs(test);
test2 = container.Resolve<ITest2>();
test3 = container.Resolve<Test3>();
}
Expand Down Expand Up @@ -573,10 +573,10 @@ public void DisposeTests_TrackTransientDisposal_Implementation_Has_Disposable()
[TestMethod]
public void DisposeTests_Instance_TrackTransient()
{
ITest1 test = new Test1();
var test = new Test1();
using (var container = new StashboxContainer(config => config.WithDisposableTransientTracking()))
{
container.RegisterInstance(test);
container.RegisterInstanceAs<ITest1>(test);

Assert.AreSame(test, container.Resolve<ITest1>());
}
Expand All @@ -590,7 +590,7 @@ public void DisposeTests_WireUp_TrackTransient()
ITest1 test = new Test1();
using (var container = new StashboxContainer(config => config.WithDisposableTransientTracking()))
{
container.WireUp(test);
container.WireUpAs(test);

Assert.AreSame(test, container.Resolve<ITest1>());
}
Expand All @@ -604,7 +604,7 @@ public void DisposeTests_WireUp_TrackTransient_WithoutDisposal()
ITest1 test = new Test1();
using (var container = new StashboxContainer(config => config.WithDisposableTransientTracking()))
{
container.WireUp(test, withoutDisposalTracking: true);
container.WireUpAs(test, withoutDisposalTracking: true);

Assert.AreSame(test, container.Resolve<ITest1>());
}
Expand Down
2 changes: 1 addition & 1 deletion src/stashbox.tests/FuncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ public void FuncTests_Register_FuncDelegate_Resolver()
{
var container = new StashboxContainer();
var test1 = new Test();
container.RegisterInstance<ITest>(test1);
container.RegisterInstanceAs<ITest>(test1);
container.RegisterFunc<string, RegisteredFuncTest2>((name, resolver) => new RegisteredFuncTest2(name, resolver.Resolve<ITest>()));

var test = container.Resolve<Func<string, RegisteredFuncTest2>>()("test");
Expand Down
2 changes: 1 addition & 1 deletion src/stashbox.tests/InjectionMemberTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void InjectionMemberTests_BuildUp()
container.RegisterType<ITest, Test>();

var test1 = new Test1();
container.WireUp<ITest1>(test1);
container.WireUpAs<ITest1>(test1);

var inst = container.Resolve<ITest1>();

Expand Down
4 changes: 2 additions & 2 deletions src/stashbox.tests/InstanceBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void InstanceBuilderTests_Resolve()
using (var container = new StashboxContainer())
{
var dep = new Test();
container.RegisterInstance<ITest>(dep);
container.RegisterInstanceAs<ITest>(dep);
var inst = container.Resolve<ITest>();

Assert.AreSame(inst, dep);
Expand All @@ -24,7 +24,7 @@ public void InstanceBuilderTests_DependencyResolve()
using (var container = new StashboxContainer())
{
var dep = new Test();
container.RegisterInstance<ITest>(dep);
container.RegisterInstanceAs<ITest>(dep);
container.RegisterType<ITest1, Test1>();

var inst = container.Resolve<ITest1>();
Expand Down
10 changes: 5 additions & 5 deletions src/stashbox.tests/WireUpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ public void WireUp_Multiple()
using (var container = new StashboxContainer())
{
var test1 = new Test1();
container.WireUp<ITest1>(test1);
container.WireUpAs<ITest1>(test1);

var test2 = new Test();
container.WireUp<ITest>(test2);
container.WireUpAs<ITest>(test2);

var inst = container.Resolve<ITest1>();
var inst2 = container.Resolve<ITest>();
Expand All @@ -34,7 +34,7 @@ public void InjectionMemberTests_WireUp()
container.RegisterType<ITest, Test>();

var test1 = new Test1();
container.WireUp<ITest1>(test1);
container.WireUpAs<ITest1>(test1);

container.RegisterType<Test2>();

Expand All @@ -56,7 +56,7 @@ public void InjectionMemberTests_WireUp_ServiceUpdated()
container.RegisterType<ITest, Test>();

var test1 = new Test1();
container.WireUp<ITest1>(test1);
container.WireUpAs<ITest1>(test1);

container.ReMap<ITest, Test>();

Expand All @@ -79,7 +79,7 @@ public void InjectionMemberTests_WireUp_WithoutService()
{
container.RegisterType<ITest, Test>();
var test1 = new Test1();
container.WireUp(test1);
container.WireUpAs(test1);
var inst = container.Resolve<Test1>();

Assert.IsNotNull(inst);
Expand Down
49 changes: 42 additions & 7 deletions src/stashbox/BuildUp/Expressions/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,9 @@ public Expression CreateFillExpression(IServiceRegistration serviceRegistration,

public Expression CreateExpression(IServiceRegistration serviceRegistration, ResolutionInfo resolutionInfo, Type serviceType)
{
var rule = serviceRegistration.RegistrationContext.ConstructorSelectionRule ?? this.containerContext.ContainerConfigurator.ContainerConfiguration.ConstructorSelectionRule;
var constructors = rule(serviceRegistration.MetaInformation.Constructors).ToArray();

var constructor = this.SelectConstructor(serviceRegistration, resolutionInfo, constructors);
if (constructor == null) return null;

Expression initExpression = Expression.New(constructor.Constructor, constructor.Parameters);
var initExpression = this.CreateInitExpression(serviceRegistration, resolutionInfo);
if (initExpression == null)
return null;

if (serviceRegistration.MetaInformation.InjectionMembers.Length > 0)
initExpression = Expression.MemberInit((NewExpression)initExpression, this.GetMemberBindings(serviceRegistration, resolutionInfo));
Expand All @@ -68,6 +64,45 @@ public Expression CreateExpression(IServiceRegistration serviceRegistration, Res
return initExpression;
}

private Expression CreateInitExpression(IServiceRegistration serviceRegistration, ResolutionInfo resolutionInfo)
{
if (serviceRegistration.RegistrationContext.SelectedConstructor != null)
{
if (serviceRegistration.RegistrationContext.ConstructorArguments != null)
return Expression.New(serviceRegistration.RegistrationContext.SelectedConstructor, serviceRegistration.RegistrationContext.ConstructorArguments.Select(Expression.Constant));

var resolutionConstructor = this.CreateResolutionConstructor(serviceRegistration, resolutionInfo, serviceRegistration.RegistrationContext.SelectedConstructor);
return Expression.New(resolutionConstructor.Constructor, resolutionConstructor.Parameters);
}

var rule = serviceRegistration.RegistrationContext.ConstructorSelectionRule ?? this.containerContext.ContainerConfigurator.ContainerConfiguration.ConstructorSelectionRule;
var constructors = rule(serviceRegistration.MetaInformation.Constructors).ToArray();

var constructor = this.SelectConstructor(serviceRegistration, resolutionInfo, constructors);
return constructor == null ? null : Expression.New(constructor.Constructor, constructor.Parameters);
}

private ResolutionConstructor CreateResolutionConstructor(IServiceRegistration serviceRegistration, ResolutionInfo resolutionInfo, ConstructorInfo constructor)
{
var parameters = constructor.GetParameters();
var paramLength = constructor.GetParameters().Length;
var parameterExpressions = new Expression[paramLength];

for (var i = 0; i < paramLength; i++)
{
var parameter = parameters[i];

var expression = this.containerContext.ResolutionStrategy.BuildResolutionExpression(this.containerContext,
resolutionInfo, serviceRegistration.MetaInformation.GetTypeInformationForParameter(parameter),
serviceRegistration.RegistrationContext.InjectionParameters);

parameterExpressions[i] = expression ?? throw new ResolutionFailedException(serviceRegistration.ImplementationType,
$"Constructor {constructor}, unresolvable parameter: ({parameter.ParameterType}){parameter.Name}");
}

return new ResolutionConstructor { Constructor = constructor, Parameters = parameterExpressions };
}

private ResolutionConstructor SelectConstructor(IServiceRegistration serviceRegistration, ResolutionInfo resolutionInfo, ConstructorInformation[] constructors)
{
var length = constructors.Length;
Expand Down
24 changes: 24 additions & 0 deletions src/stashbox/Exceptions/ConstructorNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Stashbox.Exceptions
{
/// <summary>
/// Represents a constructor not found exception.
/// </summary>
public class ConstructorNotFoundException : Exception
{
/// <summary>
/// Constructs a <see cref="ConstructorNotFoundException"/>.
/// </summary>
/// <param name="type">The type on the constructor was not found.</param>
/// <param name="argumentsLength">The length of the arguments.</param>
/// <param name="innerException">The inner exception</param>
public ConstructorNotFoundException(Type type, int argumentsLength, Exception innerException = null) :
base($"Constructor not found for {type.FullName} with {argumentsLength} arguments.", innerException)
{
}
}
}
Loading

0 comments on commit a0ec1ee

Please sign in to comment.