diff --git a/.version b/.version index 1b03fe63..6de6d055 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.5.6 \ No newline at end of file +2.5.7 \ No newline at end of file diff --git a/coverage.ps1 b/coverage.ps1 index 0ced4f62..4fc30292 100644 --- a/coverage.ps1 +++ b/coverage.ps1 @@ -2,18 +2,12 @@ exit 0 } -nuget install OpenCover -Version 4.6.519 -OutputDirectory .\tools -nuget install ReportGenerator -Version 2.5.2 -OutputDirectory .\tools +choco install opencover.portable +choco install codecov -$openCoverPath = Join-Path $PSScriptRoot "tools\OpenCover.4.6.519\tools\OpenCover.Console.exe" -$reportGeneratorPath = Join-Path $PSScriptRoot "tools\ReportGenerator.2.5.2\tools\ReportGenerator.exe" $testPath = Join-Path $PSScriptRoot "src\stashbox.tests\stashbox.tests.csproj" $coverageReportDir = Join-Path $PSScriptRoot "coverageresults" -$env:Path += ";c:\Python34;C:\Python34\Scripts" - $arguments = "-returntargetcode", "-register:user", "`"-filter:+[*]Stashbox.* -[Stashbox.Tests]* -[Stashbox]*.Utils* -[Stashbox]*.Expressions.Compile*`"", "-target:dotnet.exe", "`"-targetargs:test $testPath -f net45 -c Release`"", "-output:coverage.xml", "-skipautoprops", "-hideskipped:All" -. $openCoverPath $arguments -. pip install codecov -. codecov -f coverage.xml -X gcov -. $reportGeneratorPath -verbosity:Info -reports:coverage.xml -targetdir:$coverageReportDir "-assemblyfilters:-Stashbox.Tests*" \ No newline at end of file +. OpenCover.Console.exe $arguments +. codecov -f coverage.xml \ No newline at end of file diff --git a/src/stashbox.tests/IssueTests/37_Resolver_factory_invoke_doesnt_pass_different_parameters_given_when_theyre_the_same_type.cs b/src/stashbox.tests/IssueTests/37_Resolver_factory_invoke_doesnt_pass_different_parameters_given_when_theyre_the_same_type.cs new file mode 100644 index 00000000..dfe61ff9 --- /dev/null +++ b/src/stashbox.tests/IssueTests/37_Resolver_factory_invoke_doesnt_pass_different_parameters_given_when_theyre_the_same_type.cs @@ -0,0 +1,44 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace Stashbox.Tests.IssueTests +{ + [TestClass] + public class ResolverFactoryIssue + { + [TestMethod] + public void Resolver_factory_invoke_doesnt_pass_different_parameters_given_when_theyre_the_same_type() + { + var factory = new StashboxContainer() + .RegisterType() + .ResolveFactory(typeof(IFoo), parameterTypes: new[] { typeof(string), typeof(string) }); + + Assert.AreEqual("foobar", ((IFoo)factory.DynamicInvoke("foo", "bar")).Result); + } + + [TestMethod] + public void Resolver_factory_invoke_doesnt_pass_different_parameters_given_when_theyre_the_same_type_func() + { + var factory = new StashboxContainer() + .RegisterType() + .Resolve>(); + + Assert.AreEqual("foobar", factory("foo", "bar").Result); + } + + interface IFoo + { + string Result { get; set; } + } + + class Foobar : IFoo + { + public Foobar(string x, string y) + { + this.Result = x + y; + } + + public string Result { get; set; } + } + } +} diff --git a/src/stashbox/BuildUp/DefaultObjectBuilder.cs b/src/stashbox/BuildUp/DefaultObjectBuilder.cs index dbe5a701..1d90970e 100644 --- a/src/stashbox/BuildUp/DefaultObjectBuilder.cs +++ b/src/stashbox/BuildUp/DefaultObjectBuilder.cs @@ -4,7 +4,6 @@ using Stashbox.Infrastructure; using Stashbox.Infrastructure.Registration; using Stashbox.Resolution; -using Stashbox.Utils; using System; using System.Linq.Expressions; diff --git a/src/stashbox/BuildUp/Resolution/FuncResolver.cs b/src/stashbox/BuildUp/Resolution/FuncResolver.cs index 20a0bf8a..6959e6c2 100644 --- a/src/stashbox/BuildUp/Resolution/FuncResolver.cs +++ b/src/stashbox/BuildUp/Resolution/FuncResolver.cs @@ -34,7 +34,7 @@ public override Expression GetExpression(IContainerContext containerContext, Typ var parameters = this.PrepareExtraParameters(wrappedType, resolutionContext, args); var expression = containerContext.ResolutionStrategy.BuildResolutionExpression(containerContext, resolutionContext, funcArgumentInfo, null); - return expression != null ? expression.AsLambda(parameters) : null; + return expression?.AsLambda(parameters); } public override Expression[] GetExpressions(IContainerContext containerContext, TypeInformation typeInfo, ResolutionContext resolutionContext) @@ -72,9 +72,9 @@ private ParameterExpression[] PrepareExtraParameters(Type wrappedType, Resolutio var argName = wrappedType.Name + argType.Name + i; var parameter = argType.AsParameter(argName); parameters[i] = parameter; - resolutionContext.AddParameterExpressions(parameter); } + resolutionContext.AddParameterExpressions(wrappedType, parameters); return parameters; } } diff --git a/src/stashbox/BuildUp/Resolution/LazyResolver.cs b/src/stashbox/BuildUp/Resolution/LazyResolver.cs index a0cb448c..17a8b73d 100644 --- a/src/stashbox/BuildUp/Resolution/LazyResolver.cs +++ b/src/stashbox/BuildUp/Resolution/LazyResolver.cs @@ -75,12 +75,18 @@ public override Expression[] GetExpressions(IContainerContext containerContext, private static Expression CreateLazyExpressionCall(IContainerContext containerContext, IServiceRegistration serviceRegistration, Type type, ConstructorInfo constructor, ResolutionContext resolutionContext) { var arguments = resolutionContext.ParameterExpressions != null - ? new Expression[resolutionContext.ParameterExpressions.Length] + ? new Expression[resolutionContext.ParameterExpressions.Sum(x => x.Length)] : new Expression[0]; if (resolutionContext.ParameterExpressions != null) + { + var index = 0; for (var i = 0; i < resolutionContext.ParameterExpressions.Length; i++) - arguments[i] = resolutionContext.ParameterExpressions[i].ConvertTo(typeof(object)); + for (var j = 0; j < resolutionContext.ParameterExpressions[i].Length; j++) + arguments[index++] = resolutionContext.ParameterExpressions[i][j].ConvertTo(typeof(object)); + } + + var callExpression = DelegateCacheMethod.InvokeMethod( containerContext.AsConstant(), @@ -100,7 +106,7 @@ public override bool CanUseForResolution(IContainerContext containerContext, Typ private static object CreateLazyDelegate(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type type, object[] arguments) { var expr = serviceRegistration.GetExpression(containerContext, resolutionContext, type); - return expr.AsLambda(resolutionContext.ParameterExpressions) + return expr.AsLambda(resolutionContext.ParameterExpressions.SelectMany(x => x)) .CompileDynamicDelegate(resolutionContext)(resolutionContext.ResolutionScope).DynamicInvoke(arguments); } } diff --git a/src/stashbox/Registration/Extensions/DependencyRegistratorExtensions.cs b/src/stashbox/Registration/Extensions/DependencyRegistratorExtensions.cs index 1d3b3270..b13ee0f3 100644 --- a/src/stashbox/Registration/Extensions/DependencyRegistratorExtensions.cs +++ b/src/stashbox/Registration/Extensions/DependencyRegistratorExtensions.cs @@ -1,5 +1,5 @@ -using System; -using Stashbox.Infrastructure; +using Stashbox.Infrastructure; +using System; namespace Stashbox { @@ -37,8 +37,8 @@ public static IStashboxContainer WireUp(this IDependencyRegistrator registrator, /// Type that will be returned. /// The dependency registrator. /// The name of the registration. - /// The which on this method was called. - public static IDependencyRegistrator RegisterSingleton(this IDependencyRegistrator registrator, object name = null) + /// The which on this method was called. + public static IStashboxContainer RegisterSingleton(this IDependencyRegistrator registrator, object name = null) where TFrom : class where TTo : class, TFrom => registrator.RegisterType(context => context.WithName(name).WithSingletonLifetime()); @@ -49,8 +49,8 @@ public static IDependencyRegistrator RegisterSingleton(this IDepende /// Type that will be returned. /// The dependency registrator. /// The name of the registration. - /// The which on this method was called. - public static IDependencyRegistrator RegisterSingleton(this IDependencyRegistrator registrator, object name = null) + /// The which on this method was called. + public static IStashboxContainer RegisterSingleton(this IDependencyRegistrator registrator, object name = null) where TTo : class => registrator.RegisterType(context => context.WithName(name).WithSingletonLifetime()); @@ -61,8 +61,8 @@ public static IDependencyRegistrator RegisterSingleton(this IDependencyRegi /// Type that will be requested. /// Type that will be returned. /// The name of the registration. - /// The which on this method was called. - public static IDependencyRegistrator RegisterSingleton(this IDependencyRegistrator registrator, Type typeFrom, Type typeTo, object name = null) => + /// The which on this method was called. + public static IStashboxContainer RegisterSingleton(this IDependencyRegistrator registrator, Type typeFrom, Type typeTo, object name = null) => registrator.RegisterType(typeFrom, typeTo, context => context.WithName(name).WithSingletonLifetime()); /// @@ -72,8 +72,8 @@ public static IDependencyRegistrator RegisterSingleton(this IDependencyRegistrat /// Type that will be returned. /// The dependency registrator. /// The name of the registration. - /// The which on this method was called. - public static IDependencyRegistrator RegisterScoped(this IDependencyRegistrator registrator, object name = null) + /// The which on this method was called. + public static IStashboxContainer RegisterScoped(this IDependencyRegistrator registrator, object name = null) where TFrom : class where TTo : class, TFrom => registrator.RegisterType(context => context.WithName(name).WithScopedLifetime()); @@ -85,8 +85,8 @@ public static IDependencyRegistrator RegisterScoped(this IDependency /// Type that will be returned. /// The dependency registrator. /// The name of the registration. - /// The which on this method was called. - public static IDependencyRegistrator RegisterScoped(this IDependencyRegistrator registrator, Type typeFrom, Type typeTo, object name = null) => + /// The which on this method was called. + public static IStashboxContainer RegisterScoped(this IDependencyRegistrator registrator, Type typeFrom, Type typeTo, object name = null) => registrator.RegisterType(typeFrom, typeTo, context => context.WithName(name).WithScopedLifetime()); /// @@ -95,8 +95,8 @@ public static IDependencyRegistrator RegisterScoped(this IDependencyRegistrator /// Type that will be returned. /// The dependency registrator. /// The name of the registration. - /// The which on this method was called. - public static IDependencyRegistrator RegisterScoped(this IDependencyRegistrator registrator, object name = null) + /// The which on this method was called. + public static IStashboxContainer RegisterScoped(this IDependencyRegistrator registrator, object name = null) where TTo : class => registrator.RegisterType(context => context.WithName(name).WithScopedLifetime()); } diff --git a/src/stashbox/Resolution/ResolutionContext.cs b/src/stashbox/Resolution/ResolutionContext.cs index 07106b65..6293f763 100644 --- a/src/stashbox/Resolution/ResolutionContext.cs +++ b/src/stashbox/Resolution/ResolutionContext.cs @@ -32,7 +32,7 @@ public static ResolutionContext New(IResolutionScope scope, bool nullResultAllow private AvlTree expressionOverrides; private AvlTree currentlyDecoratingTypes; private AvlTreeKeyValue circularDependencyBarrier; - + private readonly ArrayStoreKeyed knownVariables; internal IResolutionScope ResolutionScope { get; } @@ -42,19 +42,19 @@ public static ResolutionContext New(IResolutionScope scope, bool nullResultAllow internal IContainerContext ChildContext { get; } internal ISet ScopeNames { get; } - internal ArrayStore ParameterExpressions { get; private set; } + internal ArrayStore> ParameterExpressions { get; private set; } internal ArrayStore SingleInstructions { get; private set; } internal ArrayStoreKeyed DefinedVariables { get; private set; } private ResolutionContext(IResolutionScope scope, bool nullResultAllowed) - : this(scope, AvlTreeKeyValue.Empty, AvlTree.Empty, AvlTree.Empty, ArrayStore.Empty, scope.GetActiveScopeNames(), + : this(scope, AvlTreeKeyValue.Empty, AvlTree.Empty, AvlTree.Empty, ArrayStore>.Empty, scope.GetActiveScopeNames(), null, nullResultAllowed, Constants.ResolutionScopeParameter, ArrayStoreKeyed.Empty) { } private ResolutionContext(IResolutionScope scope, AvlTreeKeyValue circularDependencyBarrier, AvlTree expressionOverrides, - AvlTree currentlyDecoratingTypes, ArrayStore parameterExpressions, ISet scopeNames, + AvlTree currentlyDecoratingTypes, ArrayStore> parameterExpressions, ISet scopeNames, IContainerContext childContext, bool nullResultAllowed, ParameterExpression currentScope, ArrayStoreKeyed knownVariables) { this.DefinedVariables = ArrayStoreKeyed.Empty; @@ -117,8 +117,14 @@ internal Expression GetExpressionOverrideOrDefault(Type type) => internal void SetExpressionOverride(Type type, Expression expression) => this.expressionOverrides = this.expressionOverrides.AddOrUpdate(type.GetHashCode(), expression, (oldValue, newValue) => newValue); - internal void AddParameterExpressions(params ParameterExpression[] parameterExpressions) => - this.ParameterExpressions = this.ParameterExpressions.AddRange(parameterExpressions); + internal void AddParameterExpressions(Type scopeType, ParameterExpression[] parameterExpressions) + { + var length = parameterExpressions.Length; + var newItems = new KeyValue[length]; + for (var i = 0; i < length; i++) + newItems[i] = new KeyValue(false, parameterExpressions[i]); + this.ParameterExpressions = this.ParameterExpressions.Add(new ArrayStoreKeyed(newItems)); + } internal void SetCircularDependencyBarrier(int key, bool value) => Swap.SwapValue(ref this.circularDependencyBarrier, barrier => barrier.AddOrUpdate(key, value, (old, @new) => @new)); diff --git a/src/stashbox/Resolution/ResolutionStrategy.cs b/src/stashbox/Resolution/ResolutionStrategy.cs index c7b524e8..5e6ada19 100644 --- a/src/stashbox/Resolution/ResolutionStrategy.cs +++ b/src/stashbox/Resolution/ResolutionStrategy.cs @@ -22,8 +22,20 @@ public Expression BuildResolutionExpression(IContainerContext containerContext, if (typeInformation.Type == Constants.ResolverType) return resolutionContext.CurrentScopeParameter.ConvertTo(Constants.ResolverType); - if (resolutionContext.ParameterExpressions.Length > 0 && resolutionContext.ParameterExpressions.Any(p => p.Type == typeInformation.Type || p.Type.Implements(typeInformation.Type))) - return resolutionContext.ParameterExpressions.Last(p => p.Type == typeInformation.Type || p.Type.Implements(typeInformation.Type)); + if (resolutionContext.ParameterExpressions.Length > 0) + { + var length = resolutionContext.ParameterExpressions.Length; + for (var i = length; i-- > 0;) + { + var parameters = resolutionContext.ParameterExpressions[i].WhereOrDefault(p => p.Value.Type == typeInformation.Type || + p.Value.Type.Implements(typeInformation.Type)); + + if (parameters == null) continue; + var selected = parameters.Repository.FirstOrDefault(parameter => !parameter.Key) ?? parameters.Repository.Last(); + selected.Key = true; + return selected.Value; + } + } var matchingParam = injectionParameters?.FirstOrDefault(param => param.Name == typeInformation.ParameterName); if (matchingParam != null) diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index aa6ccd2e..df4802b1 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -291,7 +291,7 @@ private object Activate(ResolutionContext resolutionContext, Type type, int hash private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, object name, bool nullResultAllowed, int hash) { var resolutionContext = ResolutionContext.New(resolutionScope, nullResultAllowed); - resolutionContext.AddParameterExpressions(parameterTypes.Select(p => p.AsParameter()).ToArray()); + resolutionContext.AddParameterExpressions(type, parameterTypes.Select(p => p.AsParameter()).ToArray()); var typeInfo = new TypeInformation { Type = type, DependencyName = name }; var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(typeInfo, resolutionContext); @@ -306,7 +306,7 @@ private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IReso else throw new ResolutionFailedException(type); - var expression = initExpression.AsLambda(resolutionContext.ParameterExpressions); + var expression = initExpression.AsLambda(resolutionContext.ParameterExpressions.SelectMany(x => x)); var factory = expression.CompileDynamicDelegate(resolutionContext); Swap.SwapValue(ref this.factoryDelegates[hash & this.indexBound], c => c.AddOrUpdate(hash, name ?? type, factory)); diff --git a/src/stashbox/stashbox.csproj b/src/stashbox/stashbox.csproj index d49328cb..703f55f8 100644 --- a/src/stashbox/stashbox.csproj +++ b/src/stashbox/stashbox.csproj @@ -26,6 +26,10 @@ false portable + v2.5.7 + - fixed: #37 Resolver factory invoke doesn't pass different parameters given when they're the same type + v2.5.6 + - fixed: #36 Nullreference on fetching custom attributes under netcoreapp1.1 target v2.5.5 - fixed: #35 Mixture of named and non named registrations result in the wrong type resolved - fixed: #34 Resolution from parent container not working well