Permalink
Browse files

Added support for fluent constructor selection

  • Loading branch information...
1 parent dc37af7 commit ba28cb3da339b04bb40429341e2150376ec068d9 @remogloor remogloor committed May 28, 2011
View
@@ -28,6 +28,7 @@
<include name="mscorlib.dll"/>
<include name="System.dll"/>
<include name="System.Core.dll"/>
+ <include name="${path.lib}/System.Linq.dll" if="${string::contains(build.platform, 'wp7')}"/>
</references>
</csc>
</target>
View
@@ -1,7 +1,9 @@
Version 2.4.0.0
---------------
- Added: Support for default parameters. If not explicit binding exists for a debendency but there is default value defined it is uesd instead.
+- Added: Support to define the constructor and constructor arguments using ToConstructor "to" overload
- Changed: ToConstant bindings are in singleton scope by default
+- Changed: Separate project for medium trust environments.
- Removed: No web builds. All builds are have no reference to web anymore
- Added default binding for IResolutionRoot that returns the kernel.
- Bugfix, Breaking change: Get all will now return all bindings and not skip unconditional ones anymore in case there is a conditional one. This is the same behavior as the version 2.0.1 and bevore had.
View
Binary file not shown.
View
Binary file not shown.
@@ -73,6 +73,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="FluentAssertions.Silverlight">
+ <HintPath>..\..\tools\FluentAssertions\Silverlight-4.0\FluentAssertions.Silverlight.dll</HintPath>
+ </Reference>
<Reference Include="Moq.Silverlight">
<HintPath>..\..\tools\moq\silverlight4\Moq.Silverlight.dll</HintPath>
</Reference>
@@ -343,9 +346,6 @@
<Compile Include="..\Ninject.Test\Unit\StartableStrategyTests.cs">
<Link>Unit\StartableStrategyTests.cs</Link>
</Compile>
- <Compile Include="..\Ninject.Test\XUnitShould.cs">
- <Link>XUnitShould.cs</Link>
- </Compile>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
@@ -87,5 +87,91 @@ public void CreationWillFailIfAllDepenciesAreMissingAndInjectAttributeIsApplied(
kernel.Unbind<IWarrior>();
}
}
+
+ [Fact]
+ public void SelectedCtorIsUsedIfDeclared()
+ {
+ using (IKernel kernel = new StandardKernel())
+ {
+ kernel.Bind<Barracks>().ToConstructor(_ => new Barracks());
+ kernel.Bind<IWeapon>().To<Sword>();
+ kernel.Bind<IWarrior>().To<Samurai>();
+
+ var barracks = kernel.Get<Barracks>();
+ barracks.Should().NotBeNull();
+ barracks.Warrior.Should().BeNull();
+ barracks.Weapon.Should().BeNull();
+ }
+ }
+
+ [Fact]
+ public void SelectedCtorIsUsedIfDeclaredWithInjectedArgument()
+ {
+ using (IKernel kernel = new StandardKernel())
+ {
+ kernel.Bind<Barracks>().ToConstructor(ctorArg => new Barracks(ctorArg.Inject<IWarrior>()));
+ kernel.Bind<IWeapon>().To<Sword>();
+ kernel.Bind<IWarrior>().To<Samurai>();
+
+ var barracks = kernel.Get<Barracks>();
+ barracks.Should().NotBeNull();
+ barracks.Warrior.Should().NotBeNull();
+ barracks.Warrior.Should().BeOfType<Samurai>();
+ barracks.Weapon.Should().BeNull();
+ }
+ }
+
+ [Fact]
+ public void WhenDefaultValuesArePassedToConstrctorSelectionTheyAreUsed()
+ {
+ using (IKernel kernel = new StandardKernel())
+ {
+ kernel.Bind<Barracks>().ToConstructor(ctorArg => new Barracks(new Ninja(new Sword()), ctorArg.Inject<IWeapon>()));
+ kernel.Bind<IWeapon>().To<Sword>();
+ kernel.Bind<IWarrior>().To<Samurai>();
+
+ var barracks = kernel.Get<Barracks>();
+ barracks.Should().NotBeNull();
+ barracks.Warrior.Should().NotBeNull();
+ barracks.Warrior.Should().BeOfType<Ninja>();
+ barracks.Weapon.Should().NotBeNull();
+ }
+ }
+
+ [Fact]
+ public void DefaultValuesAreEvaluatedForEachRequest()
+ {
+ using (IKernel kernel = new StandardKernel())
+ {
+ kernel.Bind<Barracks>().ToConstructor(_ => new Barracks(new Ninja(new Sword())));
+
+ var barracks1 = kernel.Get<Barracks>();
+ var barracks2 = kernel.Get<Barracks>();
+
+ barracks1.Warrior.Should().NotBeSameAs(barracks2.Warrior);
+ }
+ }
+
+ [Fact]
+ public void WhenLazyValuesArePassedToConstrctorSelectionTheyAreEvaluatedAtResolve()
+ {
+ using (IKernel kernel = new StandardKernel())
+ {
+ int activationCount = 0;
+ kernel.Bind<Ninja>().ToSelf().Named("1").OnActivation(inst => activationCount++);
+ kernel.Bind<Barracks>().ToConstructor(ctorArg => new Barracks(ctorArg.Context.Kernel.Get<Ninja>("1"), ctorArg.Inject<IWeapon>()));
+ kernel.Bind<IWeapon>().To<Sword>();
+ kernel.Bind<IWarrior>().To<Samurai>();
+
+ activationCount.Should().Be(0);
+ var barracks = kernel.Get<Barracks>();
+
+ barracks.Should().NotBeNull();
+ barracks.Warrior.Should().NotBeNull();
+ barracks.Warrior.Should().BeOfType<Ninja>();
+ barracks.Weapon.Should().NotBeNull();
+ activationCount.Should().Be(1);
+ }
+ }
}
}
@@ -48,11 +48,17 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="FluentAssertions.Silverlight">
+ <HintPath>..\..\tools\FluentAssertions\Silverlight-4.0-wp7\FluentAssertions.Silverlight.dll</HintPath>
+ </Reference>
<Reference Include="Microsoft.Phone" />
<Reference Include="Microsoft.Phone.Interop" />
<Reference Include="Ninject, Version=2.3.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\..\build\silverlight-4.0-wp7\release\Ninject.dll</HintPath>
</Reference>
+ <Reference Include="System.Linq">
+ <HintPath>..\..\lib\System.Linq.dll</HintPath>
+ </Reference>
<Reference Include="System.Windows" />
<Reference Include="mscorlib" />
<Reference Include="system" />
@@ -310,9 +316,6 @@
<Compile Include="..\Ninject.Test\Unit\StartableStrategyTests.cs">
<Link>Unit\StartableStrategyTests.cs</Link>
</Compile>
- <Compile Include="..\Ninject.Test\XUnitShould.cs">
- <Link>XUnitShould.cs</Link>
- </Compile>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
@@ -12,8 +12,6 @@
using System.Linq;
using Ninject.Infrastructure;
using Ninject.Infrastructure.Introspection;
-using Ninject.Infrastructure.Language;
-using Ninject.Injection;
using Ninject.Parameters;
using Ninject.Planning;
using Ninject.Planning.Directives;
@@ -24,6 +22,9 @@
namespace Ninject.Activation.Providers
{
+ using System.Reflection;
+ using Ninject.Selection.Heuristics;
+
/// <summary>
/// The standard provider for types, which activates instances via a <see cref="IPipeline"/>.
/// </summary>
@@ -42,23 +43,24 @@ public class StandardProvider : IProvider
/// <summary>
/// Gets or sets the selector component.
/// </summary>
- public ISelector Selector { get; private set; }
+ public IConstructorScorer ConstructorScorer { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="StandardProvider"/> class.
/// </summary>
/// <param name="type">The type (or prototype) of instances the provider creates.</param>
/// <param name="planner">The planner component.</param>
- /// <param name="selector">The selector component.</param>
- public StandardProvider(Type type, IPlanner planner, ISelector selector)
+ /// <param name="constructorScorer">The constructor scorer component.</param>
+ public StandardProvider(Type type, IPlanner planner, IConstructorScorer constructorScorer
+ )
{
Ensure.ArgumentNotNull(type, "type");
Ensure.ArgumentNotNull(planner, "planner");
- Ensure.ArgumentNotNull(selector, "selector");
+ Ensure.ArgumentNotNull(constructorScorer, "constructorScorer");
Type = type;
Planner = planner;
- Selector = selector;
+ ConstructorScorer = constructorScorer;
}
/// <summary>
@@ -77,7 +79,7 @@ public virtual object Create(IContext context)
throw new ActivationException(ExceptionFormatter.NoConstructorsAvailable(context));
var directives = context.Plan.GetAll<ConstructorInjectionDirective>();
- var directive = directives.OrderByDescending(option => Selector.ConstructorScorer.Score(context, option)).First();
+ var directive = directives.OrderByDescending(option => ConstructorScorer.Score(context, option)).First();
object[] arguments = directive.Targets.Select(target => GetValue(context, target)).ToArray();
return directive.Injector(arguments);
}
@@ -118,7 +120,20 @@ public Type GetImplementationType(Type service)
public static Func<IContext, IProvider> GetCreationCallback(Type prototype)
{
Ensure.ArgumentNotNull(prototype, "prototype");
- return ctx => new StandardProvider(prototype, ctx.Kernel.Components.Get<IPlanner>(), ctx.Kernel.Components.Get<ISelector>());
+ return ctx => new StandardProvider(prototype, ctx.Kernel.Components.Get<IPlanner>(), ctx.Kernel.Components.Get<ISelector>().ConstructorScorer);
+ }
+
+ /// <summary>
+ /// Gets a callback that creates an instance of the <see cref="StandardProvider"/>
+ /// for the specified type and constructor.
+ /// </summary>
+ /// <param name="prototype">The prototype the provider instance will create.</param>
+ /// <param name="constructor">The constructor.</param>
+ /// <returns>The created callback.</returns>
+ public static Func<IContext, IProvider> GetCreationCallback(Type prototype, ConstructorInfo constructor)
+ {
+ Ensure.ArgumentNotNull(prototype, "prototype");
+ return ctx => new StandardProvider(prototype, ctx.Kernel.Components.Get<IPlanner>(), new SpecificConstructorSelector(constructor));
}
}
}
@@ -159,7 +159,9 @@
<Compile Include="Planning\Bindings\Resolvers\StandardBindingResolver.cs" />
<Compile Include="Planning\Bindings\Resolvers\IBindingResolver.cs" />
<Compile Include="Selection\Heuristics\IInjectionHeuristic.cs" />
+ <Compile Include="Selection\Heuristics\SpecificConstructorSelector.cs" />
<Compile Include="Syntax\BindingRoot.cs" />
+ <Compile Include="Syntax\IConstructorArgument.cs" />
<Compile Include="Syntax\ModuleLoadExtensions.cs" />
<Compile Include="Infrastructure\IHaveKernel.cs" />
<Compile Include="IInitializable.cs" />
@@ -20,6 +20,9 @@
namespace Ninject.Planning.Bindings
{
+#if !NETCF
+ using System.Linq.Expressions;
+#endif
using Ninject.Planning.Targets;
/// <summary>
@@ -86,6 +89,27 @@ public IBindingWhenInNamedWithOrOnSyntax<T> To(Type implementation)
return this;
}
+ #if !NETCF
+ /// <summary>
+ /// Indicates that the service should be bound to the speecified constructor.
+ /// </summary>
+ /// <param name="newExpression">The expression that specifies the constructor.</param>
+ public IBindingWhenInNamedWithOrOnSyntax<T> ToConstructor(Expression<Func<IConstructorArgumentSyntax, T>> newExpression)
+ {
+ var ctorExpression = newExpression.Body as NewExpression;
+ if (ctorExpression == null)
+ {
+ throw new ArgumentException("The expression must be a constructor call.", "newExpression");
+ }
+
+ Binding.ProviderCallback = StandardProvider.GetCreationCallback(ctorExpression.Type, ctorExpression.Constructor);
+ Binding.Target = BindingTarget.Type;
+ this.AddConstructorArguments(ctorExpression, newExpression.Parameters[0]);
+
+ return this;
+ }
+#endif
+
/// <summary>
/// Indicates that the service should be bound to an instance of the specified provider type.
/// The instance will be activated via the kernel when an instance of the service is activated.
@@ -444,5 +468,51 @@ public IBindingOnSyntax<T> OnDeactivation(Action<IContext, T> action)
Binding.DeactivationActions.Add((context, instance) => action(context, (T)instance));
return this;
}
+
+#if !NETCF
+ private void AddConstructorArguments(NewExpression ctorExpression, ParameterExpression constructorArgumentSyntaxParameterExpression)
+ {
+ var parameters = ctorExpression.Constructor.GetParameters();
+
+ for (int i = 0; i < ctorExpression.Arguments.Count; i++)
+ {
+ var argument = ctorExpression.Arguments[i];
+ var argumentName = parameters[i].Name;
+
+ this.AddConstructorArgument(argument, argumentName, constructorArgumentSyntaxParameterExpression);
+ }
+ }
+
+ private void AddConstructorArgument(Expression argument, string argumentName, ParameterExpression constructorArgumentSyntaxParameterExpression)
+ {
+ var methodCall = argument as MethodCallExpression;
+ if (methodCall == null ||
+ methodCall.Method.GetGenericMethodDefinition().DeclaringType != typeof(IConstructorArgumentSyntax))
+ {
+ var compiledExpression = Expression.Lambda(argument, constructorArgumentSyntaxParameterExpression).Compile();
+ Binding.Parameters.Add(new ConstructorArgument(argumentName,
+ ctx => compiledExpression.DynamicInvoke(new ConstructorArgumentSyntax(ctx))));
+ }
+ }
+
+ private class ConstructorArgumentSyntax : IConstructorArgumentSyntax
+ {
+ public ConstructorArgumentSyntax(IContext context)
+ {
+ this.Context = context;
+ }
+
+ public T1 Inject<T1>()
+ {
+ throw new InvalidOperationException("This method is for declaration that a parameter shall be injected only!");
+ }
+
+ public IContext Context
+ {
+ get;
+ private set;
+ }
+ }
+#endif
}
}
Oops, something went wrong.

0 comments on commit ba28cb3

Please sign in to comment.