From 39b563a5a0cd7bdacb7f7fa8f0b24e786c54a15e Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Tue, 21 Mar 2017 05:43:05 +0100 Subject: [PATCH 01/27] Started refactoring the scope management --- appveyor.yml | 4 +- src/stashbox.tests/AttributeTests.cs | 4 +- .../BuildExtensionManagerTests.cs | 2 +- src/stashbox.tests/ContainerTests.cs | 3 +- src/stashbox.tests/LifetimeTests.cs | 11 +--- src/stashbox.tests/StandardResolveTests.cs | 24 +++---- .../BuildUp/CircularDependencyBarrier.cs | 19 +++--- src/stashbox/BuildUp/DefaultObjectBuilder.cs | 6 +- .../Expressions/Compile/DelegateTarget.cs | 2 +- .../Expressions/Compile/ExpressionEmitter.cs | 10 +-- .../BuildUp/Expressions/ExpressionBuilder.cs | 9 +-- src/stashbox/BuildUp/ObjectBuilderBase.cs | 15 ++++- .../Resolution/DefaultValueResolver.cs | 2 +- .../BuildUp/Resolution/FuncResolver.cs | 4 +- .../BuildUp/Resolution/LazyResolver.cs | 8 +-- .../BuildUp/Resolution/TupleResolver.cs | 6 +- src/stashbox/BuildUp/WireUpObjectBuilder.cs | 4 +- src/stashbox/Constants.cs | 22 +++++++ src/stashbox/ContainerContext.cs | 8 --- src/stashbox/Entity/ResolutionInfo.cs | 26 +++++--- .../Extensions/ExpressionExtensions.cs | 24 +++---- .../Infrastructure/IContainerContext.cs | 11 ---- .../Infrastructure/IDelegateRepository.cs | 8 +-- .../Infrastructure/IDependencyResolver.cs | 10 +-- src/stashbox/Infrastructure/ILifetime.cs | 10 +-- .../Infrastructure/IResolutionScope.cs | 33 ++++++++++ .../Infrastructure/IStashboxContainer.cs | 17 ++++- .../Resolution/IActivationContext.cs | 6 +- src/stashbox/Lifetime/LifetimeBase.cs | 5 +- src/stashbox/Lifetime/ScopedLifetime.cs | 56 ++++++++++++++-- src/stashbox/Lifetime/SingletonLifetime.cs | 8 ++- src/stashbox/Lifetime/TransientLifetime.cs | 16 ----- .../Registration/RegistrationContextBase.cs | 24 +------ .../Registration/ServiceRegistration.cs | 23 ++++--- src/stashbox/Resolution/ActivationContext.cs | 40 +++++++----- src/stashbox/Resolution/DelegateRepository.cs | 20 +++--- src/stashbox/ResolutionScope.cs | 47 ++++++++++++++ src/stashbox/ResolutionScopeBase.cs | 62 ++++++++++++++++++ src/stashbox/StashboxContainer.Registrator.cs | 4 +- src/stashbox/StashboxContainer.Resolver.cs | 32 ++------- src/stashbox/StashboxContainer.cs | 65 +++++++++---------- src/stashbox/Utils/AvlTree.cs | 28 -------- src/stashbox/Utils/ConcurrentStore.cs | 28 +++++++- src/stashbox/Utils/ConcurrentTree.cs | 46 ++++--------- src/stashbox/Utils/LinkedStore.cs | 60 +++++++++++++++++ src/stashbox/Utils/Swap.cs | 36 ++++++++++ src/stashbox/Utils/SyncTree.cs | 41 ++++++++++++ 47 files changed, 606 insertions(+), 343 deletions(-) create mode 100644 src/stashbox/Constants.cs create mode 100644 src/stashbox/Infrastructure/IResolutionScope.cs delete mode 100644 src/stashbox/Lifetime/TransientLifetime.cs create mode 100644 src/stashbox/ResolutionScope.cs create mode 100644 src/stashbox/ResolutionScopeBase.cs create mode 100644 src/stashbox/Utils/LinkedStore.cs create mode 100644 src/stashbox/Utils/Swap.cs create mode 100644 src/stashbox/Utils/SyncTree.cs diff --git a/appveyor.yml b/appveyor.yml index d6b0d5ef..2f316cf4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ artifact: /.*\.nupkg/ environment: - build_version: 2.3.1 + build_version: 2.3.2 COVERALLS_REPO_TOKEN: secure: Q9gcbZnzJ5a0YYF6mkr4QoQylIzeQmVytMnIe4WL7aPtb30SdNes7brLkvnXOirt @@ -79,7 +79,7 @@ secure: 2bITagXOj2s3bTJaGXh8/iyWtST8OQOFaMM+0GAKgZts9OjCVCiV7C+E/0SYsM6M environment: - build_version: 2.3.1 + build_version: 2.3.2 COVERALLS_REPO_TOKEN: secure: Q9gcbZnzJ5a0YYF6mkr4QoQylIzeQmVytMnIe4WL7aPtb30SdNes7brLkvnXOirt diff --git a/src/stashbox.tests/AttributeTests.cs b/src/stashbox.tests/AttributeTests.cs index 0a34884a..90d8276a 100644 --- a/src/stashbox.tests/AttributeTests.cs +++ b/src/stashbox.tests/AttributeTests.cs @@ -49,7 +49,7 @@ public void AttributeTests_Resolve_Activator() container.RegisterType("test11"); container.RegisterType("test12"); - var inst = container.ActivationContext.Activate(ResolutionInfo.New(), typeof(ITest1), "test12"); + var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(ITest1), "test12"); Assert.IsNotNull(inst); Assert.IsInstanceOfType(inst, typeof(Test12)); } @@ -62,7 +62,7 @@ public void AttributeTests_Resolve_Activator_Resolver() container.RegisterType("test11"); container.RegisterType("test12"); - var inst = container.ActivationContext.Activate(ResolutionInfo.New(), typeof(IEnumerable)); + var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(IEnumerable)); Assert.IsNotNull(inst); Assert.IsInstanceOfType(inst, typeof(IEnumerable)); Assert.AreEqual(3, ((IEnumerable)inst).Count()); diff --git a/src/stashbox.tests/BuildExtensionManagerTests.cs b/src/stashbox.tests/BuildExtensionManagerTests.cs index 39366cf7..002645d0 100644 --- a/src/stashbox.tests/BuildExtensionManagerTests.cs +++ b/src/stashbox.tests/BuildExtensionManagerTests.cs @@ -78,7 +78,7 @@ public void BuildExtensionManagerTests_CreateCopy() post.Setup(p => p.CreateCopy()).Returns(post2.Object).Verifiable(); - using (var child = container.BeginScope()) + using (var child = container.CreateChildContainer()) { child.RegisterInstance(new object()); diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index 6d61eb73..4778df1a 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -21,7 +21,7 @@ public void ContainerTests_ChildContainer() container.RegisterType(); container.RegisterType(); - var child = container.BeginScope(); + var child = container.CreateChildContainer(); child.RegisterType(); var test3 = child.Resolve(); @@ -82,7 +82,6 @@ public void ContainerTests_CheckRegistration() reg = container.ContainerContext.RegistrationRepository.GetAllRegistrations().FirstOrDefault(r => r.ImplementationType == typeof(Test1)); Assert.IsNotNull(reg); - Assert.IsInstanceOfType(reg.LifetimeManager, typeof(TransientLifetime)); } } diff --git a/src/stashbox.tests/LifetimeTests.cs b/src/stashbox.tests/LifetimeTests.cs index a138870b..24dc0554 100644 --- a/src/stashbox.tests/LifetimeTests.cs +++ b/src/stashbox.tests/LifetimeTests.cs @@ -79,19 +79,12 @@ public void LifetimeTests_Resolve_Parallel_Lazy() [TestMethod] public void LifetimeTests_StateCheck() { - var transient = new TransientLifetime(); - Assert.IsTrue(transient.IsTransient); - Assert.IsFalse(transient.IsScoped); - Assert.IsInstanceOfType(transient.Create(), typeof(TransientLifetime)); - var scoped = new ScopedLifetime(); - Assert.IsFalse(scoped.IsTransient); - Assert.IsTrue(scoped.IsScoped); + Assert.IsFalse(scoped.HandlesObjectDisposal); Assert.IsInstanceOfType(scoped.Create(), typeof(ScopedLifetime)); var singleton = new SingletonLifetime(); - Assert.IsFalse(singleton.IsTransient); - Assert.IsFalse(singleton.IsScoped); + Assert.IsTrue(singleton.HandlesObjectDisposal); Assert.IsInstanceOfType(singleton.Create(), typeof(SingletonLifetime)); } diff --git a/src/stashbox.tests/StandardResolveTests.cs b/src/stashbox.tests/StandardResolveTests.cs index 96642608..06e65785 100644 --- a/src/stashbox.tests/StandardResolveTests.cs +++ b/src/stashbox.tests/StandardResolveTests.cs @@ -142,15 +142,15 @@ public void StandardResolveTests_Resolve_Scoped() var inst = container.Resolve(); var inst2 = container.Resolve(); - Assert.AreEqual(inst, inst2); + Assert.AreSame(inst, inst2); using (var child = container.BeginScope()) { var inst3 = child.Resolve(); var inst4 = child.Resolve(); - Assert.AreNotEqual(inst, inst3); - Assert.AreEqual(inst3, inst4); + Assert.AreNotSame(inst, inst3); + Assert.AreSame(inst3, inst4); } } } @@ -166,22 +166,22 @@ public void StandardResolveTests_Resolve_Scoped_Injection() var inst = container.Resolve(); var inst2 = container.Resolve(); - Assert.AreEqual(inst.Test, inst2.Test); - Assert.AreEqual(inst.Test2, inst2.Test2); - Assert.AreEqual(inst.Test, inst2.Test2); + Assert.AreSame(inst.Test, inst2.Test); + Assert.AreSame(inst.Test2, inst2.Test2); + Assert.AreSame(inst.Test, inst2.Test2); using (var child = container.BeginScope()) { var inst3 = child.Resolve(); var inst4 = child.Resolve(); - Assert.AreNotEqual(inst.Test, inst4.Test); - Assert.AreNotEqual(inst.Test2, inst4.Test2); - Assert.AreNotEqual(inst.Test, inst4.Test2); + Assert.AreNotSame(inst.Test, inst4.Test); + Assert.AreNotSame(inst.Test2, inst4.Test2); + Assert.AreNotSame(inst.Test, inst4.Test2); - Assert.AreEqual(inst3.Test, inst4.Test); - Assert.AreEqual(inst3.Test2, inst4.Test2); - Assert.AreEqual(inst3.Test, inst4.Test2); + Assert.AreSame(inst3.Test, inst4.Test); + Assert.AreSame(inst3.Test2, inst4.Test2); + Assert.AreSame(inst3.Test, inst4.Test2); } } } diff --git a/src/stashbox/BuildUp/CircularDependencyBarrier.cs b/src/stashbox/BuildUp/CircularDependencyBarrier.cs index 24f08371..367cc93e 100644 --- a/src/stashbox/BuildUp/CircularDependencyBarrier.cs +++ b/src/stashbox/BuildUp/CircularDependencyBarrier.cs @@ -1,27 +1,26 @@ using Stashbox.Exceptions; using System; -using System.Collections.Generic; +using Stashbox.Entity; namespace Stashbox.BuildUp { internal sealed class CircularDependencyBarrier : IDisposable { - private readonly HashSet set; + private readonly ResolutionInfo resolutionInfo; private readonly Type type; - public CircularDependencyBarrier(HashSet set, Type type) + public CircularDependencyBarrier(ResolutionInfo resolutionInfo, Type type) { - if (set.Contains(type)) + var existing = resolutionInfo.CircularDependencyBarrier.GetOrDefault(type.GetHashCode()); + if (existing != null) throw new CircularDependencyException(type.FullName); - set.Add(type); - this.set = set; + resolutionInfo.CircularDependencyBarrier.AddOrUpdate(type.GetHashCode(), type); + this.resolutionInfo = resolutionInfo; this.type = type; } - public void Dispose() - { - this.set?.Remove(this.type); - } + public void Dispose() => + this.resolutionInfo.CircularDependencyBarrier.AddOrUpdate(this.type.GetHashCode(), null, (oldValue, newValue) => newValue); } } diff --git a/src/stashbox/BuildUp/DefaultObjectBuilder.cs b/src/stashbox/BuildUp/DefaultObjectBuilder.cs index 664fdc59..c2b05396 100644 --- a/src/stashbox/BuildUp/DefaultObjectBuilder.cs +++ b/src/stashbox/BuildUp/DefaultObjectBuilder.cs @@ -18,7 +18,7 @@ internal class DefaultObjectBuilder : ObjectBuilderBase private readonly IExpressionBuilder expressionBuilder; public DefaultObjectBuilder(IContainerContext containerContext, IMetaInfoProvider metaInfoProvider, - IContainerExtensionManager containerExtensionManager, IExpressionBuilder expressionBuilder, + IContainerExtensionManager containerExtensionManager, IExpressionBuilder expressionBuilder, InjectionParameter[] injectionParameters = null, bool isDecorator = false) : base(containerContext, isDecorator) { @@ -36,7 +36,7 @@ protected override Expression GetExpressionInternal(ResolutionInfo resolutionInf if (!this.containerContext.ContainerConfigurator.ContainerConfiguration.CircularDependencyTrackingEnabled) return this.CreateExpression(resolutionInfo, resolveType); - using (new CircularDependencyBarrier(resolutionInfo.CircularDependencyBarrier, this.metaInfoProvider.TypeTo)) + using (new CircularDependencyBarrier(resolutionInfo, this.metaInfoProvider.TypeTo)) return this.CreateExpression(resolutionInfo, resolveType); } @@ -44,7 +44,7 @@ private Expression CreateExpression(ResolutionInfo resolutionInfo, Type resolveT { if (!this.metaInfoProvider.TryChooseConstructor(out ResolutionConstructor constructor, resolutionInfo, this.injectionParameters)) - throw new ResolutionFailedException(this.metaInfoProvider.TypeTo.FullName); + return null; return this.expressionBuilder.CreateExpression(this.containerExtensionManager, this.containerContext, constructor, resolutionInfo, resolveType, this.injectionParameters, diff --git a/src/stashbox/BuildUp/Expressions/Compile/DelegateTarget.cs b/src/stashbox/BuildUp/Expressions/Compile/DelegateTarget.cs index 558a3eef..b2fc1d52 100644 --- a/src/stashbox/BuildUp/Expressions/Compile/DelegateTarget.cs +++ b/src/stashbox/BuildUp/Expressions/Compile/DelegateTarget.cs @@ -152,7 +152,7 @@ public static bool TryCollectConstants(this Expression expression, Constants con return ((BlockExpression)expression).TryCollectConstants(constants, parameters); case ExpressionType.Call: var call = (MethodCallExpression)expression; - return call.Object.TryCollectConstants(constants, parameters) && + return (call.Object == null || call.Object.TryCollectConstants(constants, parameters)) && call.Arguments.TryCollectConstants(constants, parameters); case ExpressionType.Invoke: var invoke = (InvocationExpression)expression; diff --git a/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs b/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs index a8196892..c13ef4df 100644 --- a/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs +++ b/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs @@ -16,12 +16,8 @@ internal static class ExpressionEmitter private static readonly MethodInfo delegateTargetProperty = typeof(Delegate).GetProperty("Target").GetGetMethod(); - public static bool TryEmit(this Expression expression, out Delegate resultDelegate) => - expression.TryEmit(typeof(Func), typeof(object), out resultDelegate, out DelegateTargetInformation delegateTarget); - - public static bool TryEmit(this LambdaExpression expression, out Delegate resultDelegate) => - expression.Body.TryEmit(expression.Type, expression.Body.Type, out resultDelegate, - out DelegateTargetInformation delegateTarget, expression.Parameters.ToArray()); + public static bool TryEmit(this Expression expression, out Delegate resultDelegate, Type delegateType, params ParameterExpression[] parameters) => + expression.TryEmit(delegateType, typeof(object), out resultDelegate, out DelegateTargetInformation delegateTarget, parameters); public static bool TryEmit(this Expression expression, Type delegateType, Type returnType, out Delegate resultDelegate, out DelegateTargetInformation delegateTarget, params ParameterExpression[] parameters) @@ -191,7 +187,7 @@ private static bool TryEmit(this BlockExpression expression, DelegateTargetInfor private static bool TryEmit(this MethodCallExpression expression, DelegateTargetInformation target, ILGenerator generator, params ParameterExpression[] parameters) { - if (!expression.Object.TryEmit(target, generator, parameters)) + if (expression.Object != null && !expression.Object.TryEmit(target, generator, parameters)) return false; return expression.Arguments.All(argument => argument.TryEmit(target, generator, parameters)) && diff --git a/src/stashbox/BuildUp/Expressions/ExpressionBuilder.cs b/src/stashbox/BuildUp/Expressions/ExpressionBuilder.cs index 5614e9cb..0e051a3b 100644 --- a/src/stashbox/BuildUp/Expressions/ExpressionBuilder.cs +++ b/src/stashbox/BuildUp/Expressions/ExpressionBuilder.cs @@ -11,13 +11,6 @@ namespace Stashbox.BuildUp.Expressions { internal class ExpressionBuilder : IExpressionBuilder { - private readonly MethodInfo buildExtensionMethod; - - public ExpressionBuilder() - { - this.buildExtensionMethod = typeof(IContainerExtensionManager).GetSingleMethod("ExecutePostBuildExtensions"); - } - public Expression CreateFillExpression(IContainerExtensionManager extensionManager, IContainerContext containerContext, Expression instance, ResolutionInfo resolutionInfo, Type serviceType, InjectionParameter[] parameters, ResolutionMember[] members, ResolutionMethod[] methods) { @@ -75,7 +68,7 @@ private Expression CreatePostWorkExpressionIfAny(IContainerExtensionManager exte if (extensionManager.HasPostBuildExtensions) { - var call = Expression.Call(Expression.Constant(extensionManager), buildExtensionMethod, newVariable, Expression.Constant(containerContext), + var call = Expression.Call(Expression.Constant(extensionManager), Constants.BuildExtensionMethod, newVariable, Expression.Constant(containerContext), Expression.Constant(resolutionInfo), Expression.Constant(serviceType), Expression.Constant(parameters, typeof(InjectionParameter[]))); block.Add(Expression.Assign(newVariable, Expression.Convert(call, serviceType))); diff --git a/src/stashbox/BuildUp/ObjectBuilderBase.cs b/src/stashbox/BuildUp/ObjectBuilderBase.cs index 73deab5c..f7821aa8 100644 --- a/src/stashbox/BuildUp/ObjectBuilderBase.cs +++ b/src/stashbox/BuildUp/ObjectBuilderBase.cs @@ -18,7 +18,11 @@ protected ObjectBuilderBase(IContainerContext containerContext, bool isDecorator public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - if (this.isDecorator || resolutionInfo.CurrentlyDecoratingTypes.Contains(resolveType)) + if (this.isDecorator) + return this.GetExpressionInternal(resolutionInfo, resolveType); + + var decoratedType = resolutionInfo.CurrentlyDecoratingTypes.GetOrDefault(resolveType.GetHashCode()); + if (decoratedType != null) return this.GetExpressionInternal(resolutionInfo, resolveType); var decorators = this.containerContext.DecoratorRepository.GetDecoratorsOrDefault(resolveType); @@ -34,16 +38,21 @@ public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) return this.GetExpressionInternal(resolutionInfo, resolveType); } - resolutionInfo.CurrentlyDecoratingTypes.Add(resolveType); + resolutionInfo.CurrentlyDecoratingTypes.AddOrUpdate(resolveType.GetHashCode(), resolveType); var expression = this.GetExpressionInternal(resolutionInfo, resolveType); + if (expression == null) + return null; + foreach (var decoratorRegistration in decorators) { resolutionInfo.ExpressionOverrides.AddOrUpdate(resolveType, expression, (oldValue, newValue) => newValue); expression = decoratorRegistration.GetExpression(resolutionInfo, resolveType); + if (expression == null) + return null; } - resolutionInfo.CurrentlyDecoratingTypes.Remove(resolveType); + resolutionInfo.CurrentlyDecoratingTypes.AddOrUpdate(resolveType.GetHashCode(), null, (oldValue, newValue) => newValue); return expression; } diff --git a/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs b/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs index 009afbc6..8a260451 100644 --- a/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs +++ b/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs @@ -21,7 +21,7 @@ public override Expression GetExpression(IContainerContext containerContext, Typ if (typeInfo.Type == typeof(string) || typeInfo.IsMember) return Expression.Constant(null, typeInfo.Type); - throw new ResolutionFailedException(typeInfo.Type.FullName); + return null; } public override bool CanUseForResolution(IContainerContext containerContext, TypeInformation typeInfo) => diff --git a/src/stashbox/BuildUp/Resolution/FuncResolver.cs b/src/stashbox/BuildUp/Resolution/FuncResolver.cs index 20f6a0b3..3a2bf1bb 100644 --- a/src/stashbox/BuildUp/Resolution/FuncResolver.cs +++ b/src/stashbox/BuildUp/Resolution/FuncResolver.cs @@ -42,10 +42,8 @@ public override Expression GetExpression(IContainerContext containerContext, Typ var parameters = this.PrepareExtraParameters(wrappedType, resolutionInfo, args); var expression = containerContext.ResolutionStrategy.BuildResolutionExpression(containerContext, resolutionInfo, funcArgumentInfo, null); - if (expression == null) - throw new ResolutionFailedException(typeInfo.Type.FullName); - return Expression.Lambda(expression, parameters); + return expression != null ? Expression.Lambda(expression, parameters) : null; } public override Expression[] GetExpressions(IContainerContext containerContext, TypeInformation typeInfo, ResolutionInfo resolutionInfo) diff --git a/src/stashbox/BuildUp/Resolution/LazyResolver.cs b/src/stashbox/BuildUp/Resolution/LazyResolver.cs index b4e16f93..86740712 100644 --- a/src/stashbox/BuildUp/Resolution/LazyResolver.cs +++ b/src/stashbox/BuildUp/Resolution/LazyResolver.cs @@ -109,7 +109,7 @@ public override bool CanUseForResolution(IContainerContext containerContext, Typ private class DelegateCache { private readonly Type type; - private Delegate cache; + private Func cache; public DelegateCache(Type type) { @@ -119,11 +119,11 @@ public DelegateCache(Type type) public object CreateLazyDelegate(IServiceRegistration serviceRegistration, ResolutionInfo resolutionInfo, object[] arguments) { if (this.cache != null) - return this.cache.DynamicInvoke(arguments); + return this.cache(resolutionInfo.ResolutionScope).DynamicInvoke(arguments); var expr = serviceRegistration.GetExpression(resolutionInfo, this.type); - this.cache = Expression.Lambda(expr, resolutionInfo.ParameterExpressions).CompileDelegate(); - return this.cache.DynamicInvoke(arguments); + this.cache = Expression.Lambda(expr, resolutionInfo.ParameterExpressions).CompileDelegate(Constants.ScopeExpression); + return this.cache(resolutionInfo.ResolutionScope).DynamicInvoke(arguments); } } } diff --git a/src/stashbox/BuildUp/Resolution/TupleResolver.cs b/src/stashbox/BuildUp/Resolution/TupleResolver.cs index bbc5975e..d33fb3de 100644 --- a/src/stashbox/BuildUp/Resolution/TupleResolver.cs +++ b/src/stashbox/BuildUp/Resolution/TupleResolver.cs @@ -35,7 +35,11 @@ public override Expression GetExpression(IContainerContext containerContext, Typ { var argumentInfo = new TypeInformation { Type = args[i] }; var expr = containerContext.ResolutionStrategy.BuildResolutionExpression(containerContext, resolutionInfo, argumentInfo, null); - expressions[i] = expr ?? throw new ResolutionFailedException(typeInfo.Type.FullName); + + if (expr != null) + expressions[i] = expr; + else + return null; } return Expression.New(tupleConstructor, expressions); diff --git a/src/stashbox/BuildUp/WireUpObjectBuilder.cs b/src/stashbox/BuildUp/WireUpObjectBuilder.cs index b38ff82a..40be99a8 100644 --- a/src/stashbox/BuildUp/WireUpObjectBuilder.cs +++ b/src/stashbox/BuildUp/WireUpObjectBuilder.cs @@ -43,8 +43,8 @@ protected override Expression GetExpressionInternal(ResolutionInfo resolutionInf Expression.Constant(this.instance), resolutionInfo, this.metaInfoProvider.TypeTo, this.injectionParameters, this.metaInfoProvider.GetResolutionMembers(resolutionInfo, this.injectionParameters), this.metaInfoProvider.GetResolutionMethods(resolutionInfo, this.injectionParameters)); - var factory = expr.CompileDelegate(); - this.instance = factory(); + var factory = expr.CompileDelegate(Constants.ScopeExpression); + this.instance = factory(resolutionInfo.ResolutionScope); return Expression.Constant(this.instance); } } diff --git a/src/stashbox/Constants.cs b/src/stashbox/Constants.cs new file mode 100644 index 00000000..2de6851e --- /dev/null +++ b/src/stashbox/Constants.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; +using Stashbox.Infrastructure; +using Stashbox.Infrastructure.ContainerExtension; +using Stashbox.Lifetime; + +namespace Stashbox +{ + internal static class Constants + { + public static ParameterExpression ScopeExpression = Expression.Parameter(typeof(IResolutionScope)); + + public static MethodInfo AddDisposalMethod = typeof(IResolutionScope).GetSingleMethod("AddDisposableTracking"); + + public static MethodInfo BuildExtensionMethod = typeof(IContainerExtensionManager).GetSingleMethod("ExecutePostBuildExtensions"); + + public static MethodInfo GetScopedValueMethod = typeof(ScopedLifetime).GetSingleMethod("GetScopedValue", true); + + public static Type DisposableType = typeof(IDisposable); + } +} diff --git a/src/stashbox/ContainerContext.cs b/src/stashbox/ContainerContext.cs index 1e3b6417..dec08b92 100644 --- a/src/stashbox/ContainerContext.cs +++ b/src/stashbox/ContainerContext.cs @@ -22,8 +22,6 @@ internal ContainerContext(IRegistrationRepository registrationRepository, IDeleg this.DelegateRepository = delegateRepository; this.Container = container; this.Bag = new ConcurrentKeyValueStore(); - this.ScopedRegistrations = new ConcurrentTree(); - this.TrackedTransientObjects = new ConcurrentStore(); this.ContainerConfigurator = containerConfigurator; this.DecoratorRepository = decoratorRepository; } @@ -43,15 +41,9 @@ internal ContainerContext(IRegistrationRepository registrationRepository, IDeleg /// public IResolutionStrategy ResolutionStrategy { get; } - /// - public ConcurrentTree ScopedRegistrations { get; } - /// public ConcurrentKeyValueStore Bag { get; } - /// - public ConcurrentStore TrackedTransientObjects { get; } - /// public IContainerConfigurator ContainerConfigurator { get; internal set; } diff --git a/src/stashbox/Entity/ResolutionInfo.cs b/src/stashbox/Entity/ResolutionInfo.cs index 1d4c2f90..d184d6c3 100644 --- a/src/stashbox/Entity/ResolutionInfo.cs +++ b/src/stashbox/Entity/ResolutionInfo.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; +using Stashbox.Infrastructure; using Stashbox.Utils; namespace Stashbox.Entity @@ -14,24 +15,33 @@ public class ResolutionInfo /// Static factory for . /// /// A new instance. - public static ResolutionInfo New() => new ResolutionInfo(); + public static ResolutionInfo New(IResolutionScope scope, bool nullResultAllowed = false) => new ResolutionInfo(scope, nullResultAllowed); /// /// The extra parameter expressions. /// public ParameterExpression[] ParameterExpressions { get; set; } - internal HashSet CircularDependencyBarrier { get; } + /// + /// True if null result is allowed, otherwise false. + /// + public bool NullResultAllowed { get; } + + internal SyncTree CircularDependencyBarrier { get; } + + internal SyncTree ExpressionOverrides { get; } - internal AvlTree ExpressionOverrides { get; } + internal SyncTree CurrentlyDecoratingTypes { get; } - internal HashSet CurrentlyDecoratingTypes { get; } + internal IResolutionScope ResolutionScope { get; } - internal ResolutionInfo() + internal ResolutionInfo(IResolutionScope scope, bool nullResultAllowed = false) { - this.CircularDependencyBarrier = new HashSet(); - this.ExpressionOverrides = new AvlTree(); - this.CurrentlyDecoratingTypes = new HashSet(); + this.CircularDependencyBarrier = new SyncTree(); + this.ExpressionOverrides = new SyncTree(); + this.CurrentlyDecoratingTypes = new SyncTree(); + this.NullResultAllowed = nullResultAllowed; + this.ResolutionScope = scope; } } } \ No newline at end of file diff --git a/src/stashbox/Extensions/ExpressionExtensions.cs b/src/stashbox/Extensions/ExpressionExtensions.cs index 5ab33bb4..6f7b5141 100644 --- a/src/stashbox/Extensions/ExpressionExtensions.cs +++ b/src/stashbox/Extensions/ExpressionExtensions.cs @@ -1,4 +1,6 @@ -#if NET45 || NET40 +using Stashbox.Infrastructure; + +#if NET45 || NET40 using Stashbox.BuildUp.Expressions.Compile; #endif @@ -6,33 +8,33 @@ namespace System.Linq.Expressions { internal static class ExpressionExtensions { - public static Func CompileDelegate(this Expression expression) + public static Func CompileDelegate(this Expression expression, ParameterExpression scopeParameter) { if (expression.NodeType == ExpressionType.Constant) { var instance = ((ConstantExpression)expression).Value; - return () => instance; + return scope => instance; } #if NET45 || NET40 - if (!expression.TryEmit(out Delegate factory)) - factory = Expression.Lambda(expression).Compile(); + if (!expression.TryEmit(out Delegate factory, typeof(Func), scopeParameter)) + factory = Expression.Lambda(expression, scopeParameter).Compile(); - return (Func)factory; + return (Func)factory; #else - return Expression.Lambda>(expression).Compile(); + return Expression.Lambda>(expression, scopeParameter).Compile(); #endif } - public static Delegate CompileDelegate(this LambdaExpression expression) + public static Func CompileDelegate(this LambdaExpression expression, ParameterExpression scopeParameter) { #if NET45 || NET40 - if (!expression.TryEmit(out Delegate factory)) + if (!expression.TryEmit(out Delegate factory, typeof(Func), scopeParameter)) factory = expression.Compile(); - return factory; + return (Func)factory; #else - return expression.Compile(); + return (Func)expression.Compile(); #endif } } diff --git a/src/stashbox/Infrastructure/IContainerContext.cs b/src/stashbox/Infrastructure/IContainerContext.cs index 2c0a44b3..12d6a0d0 100644 --- a/src/stashbox/Infrastructure/IContainerContext.cs +++ b/src/stashbox/Infrastructure/IContainerContext.cs @@ -1,6 +1,5 @@ using Stashbox.Infrastructure.Registration; using Stashbox.Infrastructure.Resolution; -using Stashbox.Registration; using Stashbox.Utils; namespace Stashbox.Infrastructure @@ -35,21 +34,11 @@ public interface IContainerContext /// IResolutionStrategy ResolutionStrategy { get; } - /// - /// Repository of scoped registrations. - /// - ConcurrentTree ScopedRegistrations { get; } - /// /// A generic key-value store. /// ConcurrentKeyValueStore Bag { get; } - /// - /// The transient objects which are tracked by the container for disposal. - /// - ConcurrentStore TrackedTransientObjects { get; } - /// /// Indicates that the container should track transient objects for disposal or not. /// diff --git a/src/stashbox/Infrastructure/IDelegateRepository.cs b/src/stashbox/Infrastructure/IDelegateRepository.cs index 8029c476..eb8f7772 100644 --- a/src/stashbox/Infrastructure/IDelegateRepository.cs +++ b/src/stashbox/Infrastructure/IDelegateRepository.cs @@ -13,7 +13,7 @@ public interface IDelegateRepository /// The service type. /// The service name. /// The cached factory delegate. - Func GetDelegateCacheOrDefault(Type type, string name = null); + Func GetDelegateCacheOrDefault(Type type, string name = null); /// /// Gets a cached factory method. @@ -22,7 +22,7 @@ public interface IDelegateRepository /// The parameter types. /// The service name. /// The cached factory delegate. - Delegate GetFactoryDelegateCacheOrDefault(Type type, Type[] parameterTypes, string name = null); + Func GetFactoryDelegateCacheOrDefault(Type type, Type[] parameterTypes, string name = null); /// /// Adds a service delegate into the repository. @@ -30,7 +30,7 @@ public interface IDelegateRepository /// The service type. /// The factory delegate. /// The service name. - void AddServiceDelegate(Type type, Func factory, string name = null); + void AddServiceDelegate(Type type, Func factory, string name = null); /// /// Adds a factory delegate into the repository. @@ -39,7 +39,7 @@ public interface IDelegateRepository /// The parameter type. /// The factory delegate. /// The service name. - void AddFactoryDelegate(Type type, Type[] parameterTypes, Delegate factory, string name = null); + void AddFactoryDelegate(Type type, Type[] parameterTypes, Func factory, string name = null); /// /// Invalidates a service delegate in the repository. diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index f9e8e626..14b37aaf 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -6,7 +6,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency resolver. /// - public interface IDependencyResolver + public interface IDependencyResolver : IDisposable { /// /// Resolves an instance from the container. @@ -48,13 +48,5 @@ IEnumerable ResolveAll() /// The parameter type. /// The factory delegate. Delegate ResolveFactory(Type typeFrom, string name = null, params Type[] parameterTypes); - - /// - /// Builds up an instance, the container will perform injections and extensions on it. - /// - /// The type of the requested instance. - /// The instance to build up. - /// The built object. - TTo BuildUp(TTo instance) where TTo : class; } } diff --git a/src/stashbox/Infrastructure/ILifetime.cs b/src/stashbox/Infrastructure/ILifetime.cs index 0cbca900..906b03e7 100644 --- a/src/stashbox/Infrastructure/ILifetime.cs +++ b/src/stashbox/Infrastructure/ILifetime.cs @@ -10,15 +10,9 @@ namespace Stashbox.Infrastructure public interface ILifetime { /// - /// Indicates that the lifetime manager stores or doesn't store any reference to the resolved object. - /// If it's set to true and the tracking for transient disposal objects is enabled, the registration will be copied between scopes and the output will be tracked for disposal. + /// Indicates that the lifetime handles the disposal of the resolved service. /// - bool IsTransient { get; } - - /// - /// Indicates that the registration which is registered with this lifetime should be copied between scopes and will produce a new instance in every scope. - /// - bool IsScoped { get; } + bool HandlesObjectDisposal { get; } /// /// Gets the expression for getting the instance managed by the diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs new file mode 100644 index 00000000..83deb9c1 --- /dev/null +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -0,0 +1,33 @@ +using System; + +namespace Stashbox.Infrastructure +{ + /// + /// Represents a resolution scope. + /// + public interface IResolutionScope : IDisposable + { + /// + /// Adds a service for further disposable tracking. + /// + /// The type parameter. + /// The object. + /// The object. + TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable; + + /// + /// Adds or updates an item in the scope. + /// + /// The key. + /// The value. + void AddOrUpdateScopedItem(object key, object value); + + /// + /// Gets an item from the scope. + /// + /// The key. + /// The item or null if it doesn't exists. + object GetScopedItemOrDefault(object key); + } +} diff --git a/src/stashbox/Infrastructure/IStashboxContainer.cs b/src/stashbox/Infrastructure/IStashboxContainer.cs index f4231968..5e6b0309 100644 --- a/src/stashbox/Infrastructure/IStashboxContainer.cs +++ b/src/stashbox/Infrastructure/IStashboxContainer.cs @@ -7,7 +7,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency injection container. /// - public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator, IDisposable + public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator { /// /// Registers a into the container. @@ -24,7 +24,12 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// /// Begins a new scope. /// - IStashboxContainer BeginScope(); + IDependencyResolver BeginScope(); + + /// + /// Creates a child container. + /// + IStashboxContainer CreateChildContainer(); /// /// The activation context. @@ -57,6 +62,14 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// True if the service can be resolved, otherwise false. bool CanResolve(Type typeFrom, string name = null); + /// + /// Builds up an instance, the container will perform injections and extensions on it. + /// + /// The type of the requested instance. + /// The instance to build up. + /// The built object. + TTo BuildUp(TTo instance) where TTo : class; + /// /// Configures the container. /// diff --git a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs index 9c9399fa..9b686615 100644 --- a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs +++ b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs @@ -12,9 +12,10 @@ public interface IActivationContext /// Activates a type. /// /// The service type. + /// The resolution scope. /// The service name. /// The resolved object. - object Activate(Type type, string name = null); + object Activate(Type type, IResolutionScope resolutionScope, string name = null); /// /// Activates a type. @@ -30,8 +31,9 @@ public interface IActivationContext /// /// The service type. /// The parameter types. + /// The resolution scope. /// The service name. /// The delegate which can be used for activate a type. - Delegate ActivateFactory(Type type, Type[] parameterTypes, string name = null); + Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null); } } diff --git a/src/stashbox/Lifetime/LifetimeBase.cs b/src/stashbox/Lifetime/LifetimeBase.cs index cc042bdd..373112f0 100644 --- a/src/stashbox/Lifetime/LifetimeBase.cs +++ b/src/stashbox/Lifetime/LifetimeBase.cs @@ -11,10 +11,7 @@ namespace Stashbox.Lifetime public abstract class LifetimeBase : ILifetime { /// - public virtual bool IsTransient => false; - - /// - public virtual bool IsScoped => false; + public virtual bool HandlesObjectDisposal => false; /// public virtual Expression GetExpression(IContainerContext containerContext, IObjectBuilder objectBuilder, diff --git a/src/stashbox/Lifetime/ScopedLifetime.cs b/src/stashbox/Lifetime/ScopedLifetime.cs index 85db9fe6..4579e0f0 100644 --- a/src/stashbox/Lifetime/ScopedLifetime.cs +++ b/src/stashbox/Lifetime/ScopedLifetime.cs @@ -1,16 +1,64 @@ -using Stashbox.Infrastructure; +using System; +using System.Linq.Expressions; +using Stashbox.Entity; +using Stashbox.Infrastructure; namespace Stashbox.Lifetime { /// /// Represents a scoped lifetime. /// - public class ScopedLifetime : SingletonLifetime + public class ScopedLifetime : LifetimeBase { - /// - public override bool IsScoped => true; + private volatile Expression expression; + private readonly object syncObject = new object(); + private readonly string id; + + /// + /// Constructs a . + /// + public ScopedLifetime() + { + this.id = Guid.NewGuid().ToString(); + } /// public override ILifetime Create() => new ScopedLifetime(); + + /// + public override Expression GetExpression(IContainerContext containerContext, IObjectBuilder objectBuilder, ResolutionInfo resolutionInfo, + Type resolveType) + { + if (this.expression != null) return this.expression; + lock (this.syncObject) + { + if (this.expression != null) return this.expression; + var expr = base.GetExpression(containerContext, objectBuilder, resolutionInfo, resolveType); + if (expr == null) + return null; + + var factory = expr.CompileDelegate(Constants.ScopeExpression); + + var method = Constants.GetScopedValueMethod.MakeGenericMethod(resolveType); + + this.expression = Expression.Call(method, + Constants.ScopeExpression, + Expression.Constant(factory), Expression.Constant(this.id)); + } + + return this.expression; + } + + private static TValue GetScopedValue(IResolutionScope scope, Func factory, string lifeTimeId) + { + var value = scope.GetScopedItemOrDefault(lifeTimeId); + if(value == null) + { + value = factory(scope); + scope.AddOrUpdateScopedItem(lifeTimeId, value); + } + + return (TValue)value; + } } } diff --git a/src/stashbox/Lifetime/SingletonLifetime.cs b/src/stashbox/Lifetime/SingletonLifetime.cs index e079e496..458718e7 100644 --- a/src/stashbox/Lifetime/SingletonLifetime.cs +++ b/src/stashbox/Lifetime/SingletonLifetime.cs @@ -22,16 +22,22 @@ public override Expression GetExpression(IContainerContext containerContext, IOb { if (this.expression != null) return this.expression; var expr = base.GetExpression(containerContext, objectBuilder, resolutionInfo, resolveType); + if (expr == null) + return null; + if (expr.NodeType == ExpressionType.New && ((NewExpression)expr).Arguments.Count == 0) this.instance = Activator.CreateInstance(expr.Type); else - this.instance = expr.CompileDelegate()(); + this.instance = expr.CompileDelegate(Constants.ScopeExpression)(resolutionInfo.ResolutionScope); this.expression = Expression.Constant(this.instance); } return this.expression; } + /// + public override bool HandlesObjectDisposal => true; + /// public override ILifetime Create() => new SingletonLifetime(); diff --git a/src/stashbox/Lifetime/TransientLifetime.cs b/src/stashbox/Lifetime/TransientLifetime.cs deleted file mode 100644 index fcfc7f1c..00000000 --- a/src/stashbox/Lifetime/TransientLifetime.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Stashbox.Infrastructure; - -namespace Stashbox.Lifetime -{ - /// - /// Represents a transient lifetime manager. - /// - public class TransientLifetime : LifetimeBase - { - /// - public override bool IsTransient => true; - - /// - public override ILifetime Create() => new TransientLifetime(); - } -} diff --git a/src/stashbox/Registration/RegistrationContextBase.cs b/src/stashbox/Registration/RegistrationContextBase.cs index 7b9f8c20..d328aae5 100644 --- a/src/stashbox/Registration/RegistrationContextBase.cs +++ b/src/stashbox/Registration/RegistrationContextBase.cs @@ -3,7 +3,6 @@ using Stashbox.Infrastructure; using Stashbox.Infrastructure.ContainerExtension; using Stashbox.Infrastructure.Registration; -using Stashbox.Lifetime; using Stashbox.MetaInfo; using Stashbox.Utils; using System; @@ -39,8 +38,6 @@ protected IServiceRegistration CompleteRegistration(bool update = false) { var registration = this.CreateServiceRegistration(); this.ContainerContext.RegistrationRepository.AddOrUpdateRegistration(this.TypeFrom, this.RegistrationContextData.Name, update, registration); - this.CompleteScopeManagement(update, registration.LifetimeManager, registration.ObjectBuilder); - return registration; } @@ -54,25 +51,10 @@ protected IServiceRegistration CreateServiceRegistration(bool isDecorator = fals metaInfoProvider, this.ContainerExtensionManager, this.expressionBuilder, isDecorator) : this.CreateObjectBuilder(this.ContainerExtensionManager, metaInfoProvider, isDecorator); - return this.ProduceServiceRegistration(this.TypeTo.IsOpenGenericType() ? new TransientLifetime() : registrationLifetime, objectBuilder, + return this.ProduceServiceRegistration(registrationLifetime, objectBuilder, metaInfoProvider); } - private void CompleteScopeManagement(bool update, ILifetime registrationLifetime, IObjectBuilder objectBuilder) - { - if (!registrationLifetime.IsScoped && - (!this.ContainerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled || - !registrationLifetime.IsTransient || objectBuilder.HandlesObjectDisposal)) return; - - var registrationItem = new ScopedRegistrationItem(this.TypeFrom, this.TypeTo, this.RegistrationContextData); - - if (update) - this.ContainerContext.ScopedRegistrations.AddOrUpdate(this.RegistrationContextData.Name, registrationItem, - (oldValue, newValue) => newValue); - else - this.ContainerContext.ScopedRegistrations.AddOrUpdate(this.RegistrationContextData.Name, registrationItem); - } - private IObjectBuilder CreateObjectBuilder(IContainerExtensionManager containerExtensionManager, IMetaInfoProvider metaInfoProvider, bool isDecorator = false) { if (this.RegistrationContextData.ExistingInstance != null) @@ -96,7 +78,7 @@ private IServiceRegistration ProduceServiceRegistration(ILifetime lifeTime, IObj this.RegistrationContextData.TargetTypeCondition, this.RegistrationContextData.ResolutionCondition); } - private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? new TransientLifetime() : - this.RegistrationContextData.Lifetime ?? new TransientLifetime(); + private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? null : + this.RegistrationContextData.Lifetime; } } diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index fe2f9669..4d86a999 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; namespace Stashbox.Registration { @@ -75,25 +76,23 @@ public bool ValidateGenericContraints(Type type) => !this.metaInfoProvider.HasGe public void CleanUp() { this.ObjectBuilder.CleanUp(); - this.LifetimeManager.CleanUp(); + this.LifetimeManager?.CleanUp(); } /// public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - var expr = this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); - if (!this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled || - !this.LifetimeManager.IsTransient || this.ObjectBuilder.HandlesObjectDisposal) return expr; + var expr = this.LifetimeManager == null ? this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : + this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); - var call = Expression.Call(Expression.Constant(this), "AddTransientObjectTracking", null, expr); - return Expression.Convert(call, resolveType); - } + if (!this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled || + this.LifetimeManager != null && this.LifetimeManager.HandlesObjectDisposal || + this.ObjectBuilder.HandlesObjectDisposal || + !this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) return expr; + + var method = Constants.AddDisposalMethod.MakeGenericMethod(resolveType); - private object AddTransientObjectTracking(object instance) - { - if (instance is IDisposable) - this.containerContext.TrackedTransientObjects.Add(instance); - return instance; + return Expression.Call(Constants.ScopeExpression, method, expr); } } } diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index 88138638..e6605454 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -19,45 +19,55 @@ public ActivationContext(IContainerContext containerContext, IResolverSelector r this.resolverSelector = resolverSelector; } - public object Activate(Type type, string name = null) + public object Activate(Type type, IResolutionScope resolutionScope, string name = null) { var cachedFactory = this.containerContext.DelegateRepository.GetDelegateCacheOrDefault(type, name); - return cachedFactory != null ? cachedFactory() : this.Activate(ResolutionInfo.New(), type, name); + return cachedFactory != null ? cachedFactory(resolutionScope) : this.Activate(ResolutionInfo.New(resolutionScope), type, name); } - public Delegate ActivateFactory(Type type, Type[] parameterTypes, string name = null) + public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null) { var cachedFactory = this.containerContext.DelegateRepository.GetFactoryDelegateCacheOrDefault(type, parameterTypes, name); - return cachedFactory ?? ActivateFactoryDelegate(type, parameterTypes, name); + return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name); } - - public object Activate(ResolutionInfo resolutionInfo, Type type, string name) + + public object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) { var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) { - var ragistrationFactory = registration.GetExpression(resolutionInfo, type).CompileDelegate(); + var ragistrationFactory = registration.GetExpression(resolutionInfo, type)?.CompileDelegate(Constants.ScopeExpression); + if (ragistrationFactory == null) + if (resolutionInfo.NullResultAllowed) + return null; + else + throw new ResolutionFailedException(type.FullName); + this.containerContext.DelegateRepository.AddServiceDelegate(type, ragistrationFactory, name); - return ragistrationFactory(); + return ragistrationFactory(resolutionInfo.ResolutionScope); } var expr = this.resolverSelector.GetResolverExpression(containerContext, new TypeInformation { Type = type, DependencyName = name }, resolutionInfo); if (expr == null) - throw new ResolutionFailedException(type.FullName); + if (resolutionInfo.NullResultAllowed) + return null; + else + throw new ResolutionFailedException(type.FullName); - var factory = expr.CompileDelegate(); + var factory = expr.CompileDelegate(Constants.ScopeExpression); this.containerContext.DelegateRepository.AddServiceDelegate(type, factory, name); - return factory(); + return factory(resolutionInfo.ResolutionScope); } - private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, string name = null) + private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null) { - var resolutionInfo = new ResolutionInfo + var resolutionInfo = new ResolutionInfo(resolutionScope) { ParameterExpressions = parameterTypes.Length == 0 ? null : parameterTypes.Select(Expression.Parameter).ToArray() }; var typeInfo = new TypeInformation { Type = type, DependencyName = name }; var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(typeInfo); + var initExpression = registration == null ? this.resolverSelector.GetResolverExpression(containerContext, typeInfo, resolutionInfo) : registration.GetExpression(resolutionInfo, type); @@ -65,9 +75,9 @@ private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, strin if (initExpression == null) throw new ResolutionFailedException(typeInfo.Type.FullName); - var factory = Expression.Lambda(initExpression, resolutionInfo.ParameterExpressions).CompileDelegate(); + var factory = Expression.Lambda(initExpression, resolutionInfo.ParameterExpressions).CompileDelegate(Constants.ScopeExpression); this.containerContext.DelegateRepository.AddFactoryDelegate(type, parameterTypes, factory, name); - return factory; + return factory(resolutionScope); } } } \ No newline at end of file diff --git a/src/stashbox/Resolution/DelegateRepository.cs b/src/stashbox/Resolution/DelegateRepository.cs index 0715f57f..a0b47d7c 100644 --- a/src/stashbox/Resolution/DelegateRepository.cs +++ b/src/stashbox/Resolution/DelegateRepository.cs @@ -7,35 +7,35 @@ namespace Stashbox.Resolution { internal class DelegateRepository : IDelegateRepository { - private readonly HashMap> serviceDelegates; - private readonly HashMap> keyedServiceDelegates; - private readonly HashMap factoryDelegates; + private readonly HashMap> serviceDelegates; + private readonly HashMap> keyedServiceDelegates; + private readonly HashMap> factoryDelegates; public DelegateRepository() { - this.serviceDelegates = new HashMap>(); - this.keyedServiceDelegates = new HashMap>(); - this.factoryDelegates = new HashMap(); + this.serviceDelegates = new HashMap>(); + this.keyedServiceDelegates = new HashMap>(); + this.factoryDelegates = new HashMap>(); } - public Delegate GetFactoryDelegateCacheOrDefault(Type type, Type[] parameterTypes, string name = null) + public Func GetFactoryDelegateCacheOrDefault(Type type, Type[] parameterTypes, string name = null) { var key = this.GetFactoryKey(type, parameterTypes, name); return this.factoryDelegates.GetOrDefault(type, key); } - public Func GetDelegateCacheOrDefault(Type type, string name = null) + public Func GetDelegateCacheOrDefault(Type type, string name = null) { return name == null ? this.serviceDelegates.GetOrDefault(type) : this.keyedServiceDelegates.GetOrDefault(this.GetKey(type, name)); } - public void AddFactoryDelegate(Type type, Type[] parameterTypes, Delegate factory, string name = null) + public void AddFactoryDelegate(Type type, Type[] parameterTypes, Func factory, string name = null) { var key = this.GetFactoryKey(type, parameterTypes, name); this.factoryDelegates.AddOrUpdate(type, key, factory); } - public void AddServiceDelegate(Type type, Func factory, string name = null) + public void AddServiceDelegate(Type type, Func factory, string name = null) { if (name == null) this.serviceDelegates.AddOrUpdate(type, factory); diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs new file mode 100644 index 00000000..bc0912e3 --- /dev/null +++ b/src/stashbox/ResolutionScope.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Stashbox.Infrastructure; +using Stashbox.Infrastructure.Resolution; + +namespace Stashbox +{ + /// + /// Represents a resolution scope. + /// + public class ResolutionScope : ResolutionScopeBase, IDependencyResolver + { + private readonly IActivationContext activationContext; + + /// + /// Constructs a resolution scope. + /// + /// The dependency resolver. + public ResolutionScope(IActivationContext activationContext) + { + this.activationContext = activationContext; + } + + /// + public TKey Resolve(string name = null) where TKey : class => + this.activationContext.Activate(typeof(TKey), this, name) as TKey; + + /// + public object Resolve(Type typeFrom, string name = null) => + this.activationContext.Activate(typeFrom, this, name); + + /// + public IEnumerable ResolveAll() where TKey : class => + this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + + /// + public IEnumerable ResolveAll(Type typeFrom) + { + var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); + return (IEnumerable)this.activationContext.Activate(type, this); + } + + /// + public Delegate ResolveFactory(Type typeFrom, string name = null, params Type[] parameterTypes) => + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name); + } +} diff --git a/src/stashbox/ResolutionScopeBase.cs b/src/stashbox/ResolutionScopeBase.cs new file mode 100644 index 00000000..1e5d9ab4 --- /dev/null +++ b/src/stashbox/ResolutionScopeBase.cs @@ -0,0 +1,62 @@ +using System; +using Stashbox.Infrastructure; +using Stashbox.Utils; + +namespace Stashbox +{ + /// + /// Represents a base resolution scope. + /// + public class ResolutionScopeBase : IResolutionScope + { + private readonly AtomicBool disposed; + + private readonly ConcurrentStore disposableObjects; + + private readonly ConcurrentTree scopedItems; + + /// + /// Constructs a . + /// + public ResolutionScopeBase() + { + this.disposed = new AtomicBool(); + this.disposableObjects = new ConcurrentStore(); + this.scopedItems = new ConcurrentTree(); + } + + /// + public TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable + { + this.disposableObjects.Add(disposable); + return disposable; + } + + /// + public void AddOrUpdateScopedItem(object key, object value) => + this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); + + /// + public object GetScopedItemOrDefault(object key) => + this.scopedItems.GetOrDefault(key); + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the scope. + /// + /// Indicates the scope is disposing or not. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed.CompareExchange(false, true) || !disposing) return; + foreach (var disposableObject in disposableObjects) + disposableObject?.Dispose(); + } + } +} diff --git a/src/stashbox/StashboxContainer.Registrator.cs b/src/stashbox/StashboxContainer.Registrator.cs index ad91be07..f8595331 100644 --- a/src/stashbox/StashboxContainer.Registrator.cs +++ b/src/stashbox/StashboxContainer.Registrator.cs @@ -193,7 +193,7 @@ private void WireUpInternal(object instance, string keyName, Type typeFrom, Type { var regName = NameGenerator.GetRegistrationName(typeFrom, typeTo, keyName); var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, typeTo); - var registration = new ServiceRegistration(typeFrom, typeTo, this.ContainerContext, new TransientLifetime(), + var registration = new ServiceRegistration(typeFrom, typeTo, this.ContainerContext, null, new WireUpObjectBuilder(instance, this.containerExtensionManager, this.ContainerContext, metaInfoProvider, this.expressionBuilder), metaInfoProvider); this.registrationRepository.AddOrUpdateRegistration(typeFrom, regName, false, registration); @@ -205,7 +205,7 @@ private void RegisterInstanceInternal(object instance, string keyName, Type type var instanceType = instance.GetType(); var regName = NameGenerator.GetRegistrationName(instanceType, instanceType, keyName); var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, instanceType); - var registration = new ServiceRegistration(type, instanceType, this.ContainerContext, new TransientLifetime(), + var registration = new ServiceRegistration(type, instanceType, this.ContainerContext, null, new InstanceObjectBuilder(instance, this.ContainerContext), metaInfoProvider); this.registrationRepository.AddOrUpdateRegistration(type, regName, false, registration); diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index b51c0847..dc193835 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -1,10 +1,7 @@ -using Stashbox.Entity; -using Stashbox.MetaInfo; -using Stashbox.Utils; +using Stashbox.Utils; using System; using System.Collections.Generic; -using System.Linq.Expressions; -using Stashbox.Registration; +using Stashbox.Infrastructure; namespace Stashbox { @@ -12,41 +9,26 @@ public partial class StashboxContainer { /// public TKey Resolve(string name = null) where TKey : class => - this.ActivationContext.Activate(typeof(TKey), name) as TKey; + this.ActivationContext.Activate(typeof(TKey), this, name) as TKey; /// public object Resolve(Type typeFrom, string name = null) => - this.ActivationContext.Activate(typeFrom, name); + this.ActivationContext.Activate(typeFrom, this, name); /// public IEnumerable ResolveAll() where TKey : class => - this.Resolve>(); + this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { Shield.EnsureNotNull(typeFrom, nameof(typeFrom)); var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.Resolve(type); + return (IEnumerable)this.ActivationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, params Type[] parameterTypes) => - this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, name); - - /// - public TTo BuildUp(TTo instance) where TTo : class - { - var typeTo = instance.GetType(); - var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, typeTo); - - var resolutionInfo = ResolutionInfo.New(); - var expr = this.expressionBuilder.CreateFillExpression(this.containerExtensionManager, this.ContainerContext, - Expression.Constant(instance), resolutionInfo, typeTo, null, metaInfoProvider.GetResolutionMembers(resolutionInfo), - metaInfoProvider.GetResolutionMethods(resolutionInfo)); - - var factory = expr.CompileDelegate(); - return factory() as TTo; - } + this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name); } } diff --git a/src/stashbox/StashboxContainer.cs b/src/stashbox/StashboxContainer.cs index 534f9955..d209208d 100644 --- a/src/stashbox/StashboxContainer.cs +++ b/src/stashbox/StashboxContainer.cs @@ -10,13 +10,16 @@ using Stashbox.Resolution; using Stashbox.Utils; using System; +using System.Linq.Expressions; +using Stashbox.Exceptions; +using Stashbox.MetaInfo; namespace Stashbox { /// /// Represents the stashbox dependency injection container. /// - public partial class StashboxContainer : IStashboxContainer + public partial class StashboxContainer : ResolutionScopeBase, IStashboxContainer { private readonly IContainerExtensionManager containerExtensionManager; private readonly IResolverSelector resolverSelector; @@ -77,14 +80,18 @@ public bool CanResolve(string name = null) => /// public bool CanResolve(Type typeFrom, string name = null) => - this.registrationRepository.ContainsRegistration(typeFrom, name) || + this.registrationRepository.ContainsRegistration(typeFrom, name) || this.resolverSelector.CanResolve(this.ContainerContext, new TypeInformation { Type = typeFrom, DependencyName = name }); /// public void Validate() { foreach (var serviceRegistration in this.registrationRepository.GetAllRegistrations()) - serviceRegistration.GetExpression(ResolutionInfo.New(), serviceRegistration.ServiceType); + { + var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this), serviceRegistration.ServiceType); + if (expression == null) + throw new ResolutionFailedException(serviceRegistration.ImplementationType.FullName); + } } /// @@ -97,27 +104,15 @@ public void Validate() public IActivationContext ActivationContext { get; } /// - public IStashboxContainer BeginScope() - { - var container = new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector, this.expressionBuilder); - container.OpenScope(); - return container; - } + public IStashboxContainer CreateChildContainer() => + new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector, this.expressionBuilder); /// - public void Configure(Action config) => - config?.Invoke(this.ContainerContext.ContainerConfigurator); + public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); - internal void OpenScope() - { - foreach (var registrationItem in this.ParentContainer.ContainerContext.ScopedRegistrations) - { - var registration = new ScopedRegistrationContext(registrationItem.TypeFrom, registrationItem.TypeTo, - this.ContainerContext, this.expressionBuilder, this.containerExtensionManager); - - registration.InitFromScope(registrationItem.RegistrationContextData.CreateCopy()); - } - } + /// + public void Configure(Action config) => + config?.Invoke(this.ContainerContext.ContainerConfigurator); private void RegisterResolvers() { @@ -130,31 +125,31 @@ private void RegisterResolvers() this.resolverSelector.AddResolver(new ParentContainerResolver()); } - /// - /// Disposes the container. - /// - public void Dispose() + /// + public TTo BuildUp(TTo instance) where TTo : class { - Dispose(true); - GC.SuppressFinalize(this); + var typeTo = instance.GetType(); + var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, typeTo); + + var resolutionInfo = ResolutionInfo.New(this); + var expr = this.expressionBuilder.CreateFillExpression(this.containerExtensionManager, this.ContainerContext, + Expression.Constant(instance), resolutionInfo, typeTo, null, metaInfoProvider.GetResolutionMembers(resolutionInfo), + metaInfoProvider.GetResolutionMethods(resolutionInfo)); + + var factory = expr.CompileDelegate(Constants.ScopeExpression); + return factory(this) as TTo; } /// /// Disposes the container. /// /// Indicates the container is disposing or not. - protected virtual void Dispose(bool disposing) + protected override void Dispose(bool disposing) { if (!this.disposed.CompareExchange(false, true) || !disposing) return; + base.Dispose(true); this.registrationRepository.CleanUp(); this.containerExtensionManager.CleanUp(); - - var trackedObjects = this.ContainerContext.TrackedTransientObjects.GetAll(); - foreach (var trackedObject in trackedObjects) - { - var disposable = trackedObject as IDisposable; - disposable?.Dispose(); - } } } } diff --git a/src/stashbox/Utils/AvlTree.cs b/src/stashbox/Utils/AvlTree.cs index 51fecec5..06a3cfda 100644 --- a/src/stashbox/Utils/AvlTree.cs +++ b/src/stashbox/Utils/AvlTree.cs @@ -4,34 +4,6 @@ namespace Stashbox.Utils { - internal class AvlTree : IEnumerable - { - private AvlTree repository; - - public AvlTree() - { - this.repository = new AvlTree(); - } - - public TValue GetOrDefault(TKey key) - { - var hash = key.GetHashCode(); - return this.repository.GetOrDefault(hash); - } - - public void AddOrUpdate(TKey key, TValue value, - Func updateDelegate = null) - { - var hash = key.GetHashCode(); - this.repository = this.repository.AddOrUpdate(hash, value, updateDelegate); - } - - IEnumerator IEnumerable.GetEnumerator() => this.repository.GetEnumerator(); - - public IEnumerator GetEnumerator() => this.repository.GetEnumerator(); - } - - internal class AvlTree : IEnumerable { private static readonly AvlTree Empty = new AvlTree(); diff --git a/src/stashbox/Utils/ConcurrentStore.cs b/src/stashbox/Utils/ConcurrentStore.cs index 5ae0a121..72efa946 100644 --- a/src/stashbox/Utils/ConcurrentStore.cs +++ b/src/stashbox/Utils/ConcurrentStore.cs @@ -1,18 +1,40 @@ -namespace Stashbox.Utils +using System.Collections; +using System.Collections.Generic; + +namespace Stashbox.Utils { /// /// Represents a concurrent collection. /// /// The content type generic parameter. - public class ConcurrentStore : ConcurrentKeyValueStore + public class ConcurrentStore : IEnumerable { + private readonly Swap> repository; + + /// + /// Constructs a + /// + public ConcurrentStore() + { + this.repository = new Swap>(new LinkedStore(default(TContent), null, 0)); + } + /// /// Adds an item to the collection. /// /// The item to be added. public void Add(TContent content) { - base.Add(content.GetHashCode(), content); + var current = repository.Value; + var newRepo = this.repository.Value.Add(content); + + if(!this.repository.TrySwapCurrent(current, newRepo)) + this.repository.SwapCurrent(repo => repo.Add(content)); } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + /// + public IEnumerator GetEnumerator() => this.repository.Value.GetEnumerator(); } } diff --git a/src/stashbox/Utils/ConcurrentTree.cs b/src/stashbox/Utils/ConcurrentTree.cs index aebb6b5f..bc4b51fc 100644 --- a/src/stashbox/Utils/ConcurrentTree.cs +++ b/src/stashbox/Utils/ConcurrentTree.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Threading; namespace Stashbox.Utils { @@ -93,29 +92,29 @@ public class ConcurrentTree : IEnumerable /// A new tree instance public static ConcurrentTree Create() => new ConcurrentTree(); - private AvlTree repository; + private Swap> repository; /// /// The current root value of the tree, /// - public TValue Value => this.repository.Value; + public TValue Value => this.repository.Value.Value; /// /// Inidicates that the tree has more nodes than the root one. /// - public bool HasMultipleItems => this.repository.HasMultipleItems; + public bool HasMultipleItems => this.repository.Value.HasMultipleItems; /// /// True if the tree is empty, otherwise false. /// - public bool IsEmpty => this.repository.IsEmpty; + public bool IsEmpty => this.repository.Value.IsEmpty; /// /// Constructs the /// public ConcurrentTree() { - this.repository = new AvlTree(); + this.repository = new Swap>(new AvlTree()); } /// @@ -124,7 +123,7 @@ public ConcurrentTree() /// The key of the entry. /// The found or the default value. public TValue GetOrDefault(int key) => - this.repository.GetOrDefault(key); + this.repository.Value.GetOrDefault(key); /// @@ -136,11 +135,11 @@ public TValue GetOrDefault(int key) => /// The modified tree. public ConcurrentTree AddOrUpdate(int key, TValue value, Func updateDelegate = null) { - var currentRepo = this.repository; - var newRepo = this.repository.AddOrUpdate(key, value, updateDelegate); + var currentRepo = this.repository.Value; + var newRepo = this.repository.Value.AddOrUpdate(key, value, updateDelegate); - if (!this.TrySwapCurrentRepository(currentRepo, newRepo)) - this.SwapCurrentRepository(repo => repo.AddOrUpdate(key, value, updateDelegate)); + if (!this.repository.TrySwapCurrent(currentRepo, newRepo)) + this.repository.SwapCurrent(repo => repo.AddOrUpdate(key, value, updateDelegate)); return this; } @@ -150,30 +149,11 @@ public ConcurrentTree AddOrUpdate(int key, TValue value, Func /// The reversed collection. public IEnumerable ReverseTraversal() => - this.repository.ReverseTraversal(); - - private bool TrySwapCurrentRepository(AvlTree currentRepo, AvlTree newRepo) => - Interlocked.CompareExchange(ref repository, newRepo, currentRepo) == currentRepo; - - private void SwapCurrentRepository(Func, AvlTree> repoFactory) - { - AvlTree currentRepo; - AvlTree newRepo; - var counter = 0; - - do - { - if (++counter > 20) - throw new InvalidOperationException("Swap quota exceeded."); - - currentRepo = this.repository; - newRepo = repoFactory(currentRepo); - } while (Interlocked.CompareExchange(ref repository, newRepo, currentRepo) != currentRepo); - } + this.repository.Value.ReverseTraversal(); /// - public IEnumerator GetEnumerator() => this.repository.GetEnumerator(); + public IEnumerator GetEnumerator() => this.repository.Value.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => this.repository.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => this.repository.Value.GetEnumerator(); } } diff --git a/src/stashbox/Utils/LinkedStore.cs b/src/stashbox/Utils/LinkedStore.cs new file mode 100644 index 00000000..e69900bf --- /dev/null +++ b/src/stashbox/Utils/LinkedStore.cs @@ -0,0 +1,60 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Stashbox.Utils +{ + internal class LinkedStore : IEnumerable + { + private readonly LinkedStore next; + + private readonly TValue value; + + public int Count { get; } + + public LinkedStore(TValue value, LinkedStore next, int count) + { + this.value = value; + this.Count = count; + this.next = next; + } + + public LinkedStore Add(TValue paramValue) => new LinkedStore(paramValue, this, this.Count + 1); + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + public IEnumerator GetEnumerator() => new LinkedStoreEnumerator(this); + + private class LinkedStoreEnumerator : IEnumerator + { + private readonly LinkedStore init; + private LinkedStore current; + + public LinkedStoreEnumerator(LinkedStore init) + { + this.init = init; + } + + public bool MoveNext() + { + if (this.current == null && this.init.next != null) + this.current = this.init; + else if (current?.next != null) + this.current = this.current.next; + else + return false; + + return true; + + } + + public void Reset() => this.current = this.init; + + public TValue Current => this.current.value; + + object IEnumerator.Current => this.Current; + + public void Dispose() + { } + } + } +} diff --git a/src/stashbox/Utils/Swap.cs b/src/stashbox/Utils/Swap.cs new file mode 100644 index 00000000..45a784e6 --- /dev/null +++ b/src/stashbox/Utils/Swap.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; + +namespace Stashbox.Utils +{ + internal class Swap where TValue : class + { + private TValue storedValue; + + public TValue Value => this.storedValue; + + public Swap(TValue initialValue) + { + this.storedValue = initialValue; + } + + public bool TrySwapCurrent(TValue currentValue, TValue newValue) => + Interlocked.CompareExchange(ref storedValue, newValue, currentValue) == currentValue; + + public void SwapCurrent(Func valueFactory) + { + TValue currentValue; + TValue newValue; + var counter = 0; + + do + { + if (++counter > 20) + throw new InvalidOperationException("Swap quota exceeded."); + + currentValue = this.storedValue; + newValue = valueFactory(currentValue); + } while (Interlocked.CompareExchange(ref storedValue, newValue, currentValue) != currentValue); + } + } +} diff --git a/src/stashbox/Utils/SyncTree.cs b/src/stashbox/Utils/SyncTree.cs new file mode 100644 index 00000000..3aa229b8 --- /dev/null +++ b/src/stashbox/Utils/SyncTree.cs @@ -0,0 +1,41 @@ +using System; + +namespace Stashbox.Utils +{ + internal class SyncTree + { + private AvlTree repository; + + public SyncTree() + { + this.repository = new AvlTree(); + } + + public TValue GetOrDefault(int key) => this.repository.GetOrDefault(key); + + public void AddOrUpdate(int key, TValue value, Func update = null) => + this.repository = this.repository.AddOrUpdate(key, value, update); + } + + internal class SyncTree + { + private AvlTree repository; + + public SyncTree() + { + this.repository = new AvlTree(); + } + + public TValue GetOrDefault(TKey key) + { + var hash = key.GetHashCode(); + return this.repository.GetOrDefault(hash); + } + + public void AddOrUpdate(TKey key, TValue value, Func update = null) + { + var hash = key.GetHashCode(); + this.repository = this.repository.AddOrUpdate(hash, value, update); + } + } +} From 5392f4a3dcd16092669e1df1975f50b084017593 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Tue, 21 Mar 2017 13:31:48 +0100 Subject: [PATCH 02/27] Fixed factory delegate compilation --- src/stashbox.tests/ContainerTests.cs | 1 - src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs | 4 ++-- src/stashbox/Extensions/ExpressionExtensions.cs | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index 4778df1a..39282c33 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -7,7 +7,6 @@ using System.Linq.Expressions; using Stashbox.Infrastructure.Resolution; using System.Linq; -using Stashbox.Lifetime; namespace Stashbox.Tests { diff --git a/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs b/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs index c13ef4df..f72e5d7f 100644 --- a/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs +++ b/src/stashbox/BuildUp/Expressions/Compile/ExpressionEmitter.cs @@ -16,8 +16,8 @@ internal static class ExpressionEmitter private static readonly MethodInfo delegateTargetProperty = typeof(Delegate).GetProperty("Target").GetGetMethod(); - public static bool TryEmit(this Expression expression, out Delegate resultDelegate, Type delegateType, params ParameterExpression[] parameters) => - expression.TryEmit(delegateType, typeof(object), out resultDelegate, out DelegateTargetInformation delegateTarget, parameters); + public static bool TryEmit(this Expression expression, out Delegate resultDelegate, Type delegateType, Type returnType, params ParameterExpression[] parameters) => + expression.TryEmit(delegateType, returnType, out resultDelegate, out DelegateTargetInformation delegateTarget, parameters); public static bool TryEmit(this Expression expression, Type delegateType, Type returnType, out Delegate resultDelegate, out DelegateTargetInformation delegateTarget, params ParameterExpression[] parameters) diff --git a/src/stashbox/Extensions/ExpressionExtensions.cs b/src/stashbox/Extensions/ExpressionExtensions.cs index 6f7b5141..afbc733f 100644 --- a/src/stashbox/Extensions/ExpressionExtensions.cs +++ b/src/stashbox/Extensions/ExpressionExtensions.cs @@ -17,7 +17,7 @@ public static Func CompileDelegate(this Expression exp } #if NET45 || NET40 - if (!expression.TryEmit(out Delegate factory, typeof(Func), scopeParameter)) + if (!expression.TryEmit(out Delegate factory, typeof(Func), typeof(object), scopeParameter)) factory = Expression.Lambda(expression, scopeParameter).Compile(); return (Func)factory; @@ -29,7 +29,7 @@ public static Func CompileDelegate(this Expression exp public static Func CompileDelegate(this LambdaExpression expression, ParameterExpression scopeParameter) { #if NET45 || NET40 - if (!expression.TryEmit(out Delegate factory, typeof(Func), scopeParameter)) + if (!expression.TryEmit(out Delegate factory, typeof(Func), typeof(Delegate), scopeParameter)) factory = expression.Compile(); return (Func)factory; From 08a213b64164ea81e69ce564d482d96feddddfc0 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Tue, 21 Mar 2017 13:39:52 +0100 Subject: [PATCH 03/27] Fixed expression compilation on .net standard platform --- src/stashbox/Extensions/ExpressionExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stashbox/Extensions/ExpressionExtensions.cs b/src/stashbox/Extensions/ExpressionExtensions.cs index afbc733f..87f87f41 100644 --- a/src/stashbox/Extensions/ExpressionExtensions.cs +++ b/src/stashbox/Extensions/ExpressionExtensions.cs @@ -30,11 +30,11 @@ public static Func CompileDelegate(this LambdaExpres { #if NET45 || NET40 if (!expression.TryEmit(out Delegate factory, typeof(Func), typeof(Delegate), scopeParameter)) - factory = expression.Compile(); + factory = Expression.Lambda>(expression, scopeParameter).Compile(); return (Func)factory; #else - return (Func)expression.Compile(); + return Expression.Lambda>(expression, scopeParameter).Compile(); #endif } } From 28069642b7203bb50aa6e61802eed886a6983691 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Tue, 21 Mar 2017 14:48:31 +0100 Subject: [PATCH 04/27] Refactor --- .../BuildUp/GenericTypeObjectBuilder.cs | 6 +-- .../Registration/RegistrationContextBase.cs | 12 ++++++ .../Registration/ScopedRegistrationContext.cs | 28 -------------- .../Registration/ScopedRegistrationItem.cs | 38 ------------------- 4 files changed, 15 insertions(+), 69 deletions(-) delete mode 100644 src/stashbox/Registration/ScopedRegistrationContext.cs delete mode 100644 src/stashbox/Registration/ScopedRegistrationItem.cs diff --git a/src/stashbox/BuildUp/GenericTypeObjectBuilder.cs b/src/stashbox/BuildUp/GenericTypeObjectBuilder.cs index 46b73bb8..06682654 100644 --- a/src/stashbox/BuildUp/GenericTypeObjectBuilder.cs +++ b/src/stashbox/BuildUp/GenericTypeObjectBuilder.cs @@ -40,13 +40,13 @@ protected override Expression GetExpressionInternal(ResolutionInfo resolutionInf private IServiceRegistration RegisterConcreteGenericType(Type resolveType, Type genericType) { - var registrationContext = new ScopedRegistrationContext(resolveType, genericType, this.containerContext, this.expressionBuilder, this.containerExtensionManager); + var registrationContext = new RegistrationContext(resolveType, genericType, this.containerContext, this.expressionBuilder, this.containerExtensionManager); var newData = this.registrationContextData.CreateCopy(); newData.Name = null; - if (!this.isDecorator) return registrationContext.InitFromScope(newData); + if (!this.isDecorator) return registrationContext.InitWithExistingData(newData); - var registration = registrationContext.CreateRegistration(newData, this.isDecorator); + var registration = registrationContext.CreateServiceRegistration(newData, this.isDecorator); this.containerContext.DecoratorRepository.AddDecorator(resolveType, registration); return registration; } diff --git a/src/stashbox/Registration/RegistrationContextBase.cs b/src/stashbox/Registration/RegistrationContextBase.cs index d328aae5..1b4aa4ad 100644 --- a/src/stashbox/Registration/RegistrationContextBase.cs +++ b/src/stashbox/Registration/RegistrationContextBase.cs @@ -34,6 +34,18 @@ public RegistrationContextBase(Type typeFrom, Type typeTo, IContainerContext con this.expressionBuilder = expressionBuilder; } + public IServiceRegistration InitWithExistingData(RegistrationContextData data) + { + this.RegistrationContextData = data; + return this.CompleteRegistration(); + } + + public IServiceRegistration CreateServiceRegistration(RegistrationContextData data, bool isDecorator = false) + { + this.RegistrationContextData = data; + return this.CreateServiceRegistration(isDecorator); + } + protected IServiceRegistration CompleteRegistration(bool update = false) { var registration = this.CreateServiceRegistration(); diff --git a/src/stashbox/Registration/ScopedRegistrationContext.cs b/src/stashbox/Registration/ScopedRegistrationContext.cs deleted file mode 100644 index aa3b7359..00000000 --- a/src/stashbox/Registration/ScopedRegistrationContext.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Stashbox.BuildUp.Expressions; -using Stashbox.Infrastructure; -using Stashbox.Infrastructure.ContainerExtension; -using System; -using Stashbox.Infrastructure.Registration; - -namespace Stashbox.Registration -{ - internal class ScopedRegistrationContext : RegistrationContextBase - { - public ScopedRegistrationContext(Type typeFrom, Type typeTo, IContainerContext containerContext, - IExpressionBuilder expressionBuilder, IContainerExtensionManager containerExtensionManager) - : base(typeFrom, typeTo, containerContext, expressionBuilder, containerExtensionManager) - { } - - public IServiceRegistration InitFromScope(RegistrationContextData scopeData) - { - base.RegistrationContextData = scopeData; - return base.CompleteRegistration(); - } - - public IServiceRegistration CreateRegistration(RegistrationContextData scopeData, bool isDecorator = false) - { - base.RegistrationContextData = scopeData; - return base.CreateServiceRegistration(isDecorator); - } - } -} diff --git a/src/stashbox/Registration/ScopedRegistrationItem.cs b/src/stashbox/Registration/ScopedRegistrationItem.cs deleted file mode 100644 index b399817b..00000000 --- a/src/stashbox/Registration/ScopedRegistrationItem.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Stashbox.Registration -{ - /// - /// Represents an item of the scoped registrations collection. - /// - public class ScopedRegistrationItem - { - /// - /// The type that will be requested. - /// - public Type TypeFrom { get; } - - /// - /// The type that will be returned. - /// - public Type TypeTo { get; } - - /// - /// The context data of a service registration. - /// - public RegistrationContextData RegistrationContextData { get; } - - /// - /// Constructs a . - /// - /// The type that will be requested. - /// The type that will be returned. - /// The context data of a service registration. - public ScopedRegistrationItem(Type typeFrom, Type typeTo, RegistrationContextData registrationContextData) - { - this.TypeFrom = typeFrom; - this.TypeTo = typeTo; - this.RegistrationContextData = registrationContextData; - } - } -} From ba4d14e6e389e9246a55fcbbd12c1cef37041003 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Tue, 21 Mar 2017 15:42:16 +0100 Subject: [PATCH 05/27] Fixed type conversion, added more unit tests and null result support --- src/stashbox.tests/DisposeTests.cs | 26 +++++++++++++++++++ src/stashbox.tests/ResolverTests.cs | 16 ++++++++++-- src/stashbox.tests/StandardResolveTests.cs | 23 ++++++++++++++++ src/stashbox/BuildUp/DefaultObjectBuilder.cs | 1 - .../Resolution/DefaultValueResolver.cs | 1 - .../BuildUp/Resolution/FuncResolver.cs | 1 - .../BuildUp/Resolution/LazyResolver.cs | 4 +-- .../BuildUp/Resolution/TupleResolver.cs | 1 - src/stashbox/Constants.cs | 2 ++ .../Infrastructure/IDependencyResolver.cs | 12 ++++++--- .../Resolution/IActivationContext.cs | 7 +++-- .../Registration/ServiceRegistration.cs | 4 +-- src/stashbox/Resolution/ActivationContext.cs | 12 ++++----- src/stashbox/ResolutionScope.cs | 12 ++++----- src/stashbox/StashboxContainer.Resolver.cs | 13 +++++----- 15 files changed, 100 insertions(+), 35 deletions(-) diff --git a/src/stashbox.tests/DisposeTests.cs b/src/stashbox.tests/DisposeTests.cs index 1acfa634..5d250cbe 100644 --- a/src/stashbox.tests/DisposeTests.cs +++ b/src/stashbox.tests/DisposeTests.cs @@ -224,6 +224,22 @@ public void DisposeTests_TrackTransientDisposal_Scoped_Transient_Singleton() Assert.IsTrue(test6.Test1.Disposed); } + [TestMethod] + public void DisposeTests_TrackTransientDisposal_Implementation_Has_Disposable() + { + IStashboxContainer container = new StashboxContainer(config => config.WithDisposableTransientTracking()); + ITest11 test1; + + container.RegisterType(); + + using (var child = container.BeginScope()) + { + test1 = child.Resolve(); + } + + Assert.IsTrue(((Test4)test1).Disposed); + } + [TestMethod] public void DisposeTests_Instance_TrackTransient() { @@ -277,5 +293,15 @@ public class Test3 [Dependency] public ITest1 Test1 { get; set; } } + + public class Test4 : ITest11, IDisposable + { + public bool Disposed { get; private set; } + + public void Dispose() + { + this.Disposed = true; + } + } } } \ No newline at end of file diff --git a/src/stashbox.tests/ResolverTests.cs b/src/stashbox.tests/ResolverTests.cs index 7ed702a8..6bfad43c 100644 --- a/src/stashbox.tests/ResolverTests.cs +++ b/src/stashbox.tests/ResolverTests.cs @@ -57,7 +57,7 @@ public void ResolverTests_DefaultValue_RefType_WithOptional() Assert.AreEqual(inst.I, null); } } - + [TestMethod] [ExpectedException(typeof(ResolutionFailedException))] public void ResolverTests_DefaultValue_RefType_WithOutOptional() @@ -69,6 +69,18 @@ public void ResolverTests_DefaultValue_RefType_WithOutOptional() } } + [TestMethod] + public void ResolverTests_DefaultValue_RefType_WithOutOptional_AllowNull() + { + using (var container = new StashboxContainer(config => config.WithOptionalAndDefaultValueInjection())) + { + container.RegisterType(); + var result = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(result); + } + } + [TestMethod] public void ResolverTests_DefaultValue_String() { @@ -154,7 +166,7 @@ public void ResolverTests_MemberInject_WithAutoMemberInjection() Assert.IsNotNull(inst.I); } } - + [TestMethod] public void ResolverTests_MemberInject_WithAutoMemberInjection_Field() { diff --git a/src/stashbox.tests/StandardResolveTests.cs b/src/stashbox.tests/StandardResolveTests.cs index 06e65785..644cf5c5 100644 --- a/src/stashbox.tests/StandardResolveTests.cs +++ b/src/stashbox.tests/StandardResolveTests.cs @@ -59,6 +59,18 @@ public void StandardResolveTests_DependencyResolve_ResolutionFailed() } } + [TestMethod] + public void StandardResolveTests_DependencyResolve_ResolutionFailed_AllowNull() + { + using (IStashboxContainer container = new StashboxContainer()) + { + container.RegisterType(); + var result = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(result); + } + } + [TestMethod] [ExpectedException(typeof(ResolutionFailedException))] public void StandardResolveTests_Resolve_ResolutionFailed() @@ -69,6 +81,17 @@ public void StandardResolveTests_Resolve_ResolutionFailed() } } + [TestMethod] + public void StandardResolveTests_Resolve_ResolutionFailed_AllowNull() + { + using (IStashboxContainer container = new StashboxContainer()) + { + var result = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(result); + } + } + [TestMethod] public void StandardResolveTests_Resolve_Parallel() { diff --git a/src/stashbox/BuildUp/DefaultObjectBuilder.cs b/src/stashbox/BuildUp/DefaultObjectBuilder.cs index c2b05396..cae1fff8 100644 --- a/src/stashbox/BuildUp/DefaultObjectBuilder.cs +++ b/src/stashbox/BuildUp/DefaultObjectBuilder.cs @@ -2,7 +2,6 @@ using Stashbox.BuildUp.Expressions; using Stashbox.Entity; using Stashbox.Entity.Resolution; -using Stashbox.Exceptions; using Stashbox.Infrastructure; using Stashbox.Infrastructure.ContainerExtension; using System.Linq.Expressions; diff --git a/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs b/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs index 8a260451..ef8f9731 100644 --- a/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs +++ b/src/stashbox/BuildUp/Resolution/DefaultValueResolver.cs @@ -1,5 +1,4 @@ using Stashbox.Entity; -using Stashbox.Exceptions; using System; using System.Linq.Expressions; using System.Reflection; diff --git a/src/stashbox/BuildUp/Resolution/FuncResolver.cs b/src/stashbox/BuildUp/Resolution/FuncResolver.cs index 3a2bf1bb..73922e19 100644 --- a/src/stashbox/BuildUp/Resolution/FuncResolver.cs +++ b/src/stashbox/BuildUp/Resolution/FuncResolver.cs @@ -1,5 +1,4 @@ using Stashbox.Entity; -using Stashbox.Exceptions; using Stashbox.Infrastructure; using Stashbox.Infrastructure.Resolution; using System; diff --git a/src/stashbox/BuildUp/Resolution/LazyResolver.cs b/src/stashbox/BuildUp/Resolution/LazyResolver.cs index 86740712..f369379c 100644 --- a/src/stashbox/BuildUp/Resolution/LazyResolver.cs +++ b/src/stashbox/BuildUp/Resolution/LazyResolver.cs @@ -33,7 +33,7 @@ public override Expression GetExpression(IContainerContext containerContext, Typ IsMember = typeInfo.IsMember }; - var ctorParamType = typeof(Func<>).MakeGenericType(lazyArgumentInfo.Type); + var ctorParamType = Constants.FuncType.MakeGenericType(lazyArgumentInfo.Type); var lazyConstructor = typeInfo.Type.GetConstructor(ctorParamType); var registration = containerContext.RegistrationRepository.GetRegistrationOrDefault(lazyArgumentInfo, true); @@ -59,7 +59,7 @@ public override Expression[] GetExpressions(IContainerContext containerContext, IsMember = typeInfo.IsMember }; - var ctorParamType = typeof(Func<>).MakeGenericType(lazyArgumentInfo.Type); + var ctorParamType = Constants.FuncType.MakeGenericType(lazyArgumentInfo.Type); var lazyConstructor = typeInfo.Type.GetConstructor(ctorParamType); var registrations = containerContext.RegistrationRepository.GetRegistrationsOrDefault(lazyArgumentInfo.Type); diff --git a/src/stashbox/BuildUp/Resolution/TupleResolver.cs b/src/stashbox/BuildUp/Resolution/TupleResolver.cs index d33fb3de..c8f32b57 100644 --- a/src/stashbox/BuildUp/Resolution/TupleResolver.cs +++ b/src/stashbox/BuildUp/Resolution/TupleResolver.cs @@ -4,7 +4,6 @@ using Stashbox.Infrastructure; using System.Linq.Expressions; using System.Collections.Generic; -using Stashbox.Exceptions; namespace Stashbox.BuildUp.Resolution { diff --git a/src/stashbox/Constants.cs b/src/stashbox/Constants.cs index 2de6851e..3ff866a2 100644 --- a/src/stashbox/Constants.cs +++ b/src/stashbox/Constants.cs @@ -18,5 +18,7 @@ internal static class Constants public static MethodInfo GetScopedValueMethod = typeof(ScopedLifetime).GetSingleMethod("GetScopedValue", true); public static Type DisposableType = typeof(IDisposable); + + public static Type FuncType = typeof(Func<>); } } diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index 14b37aaf..665db3fc 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -1,4 +1,5 @@ -using System; +using Stashbox.Exceptions; +using System; using System.Collections.Generic; namespace Stashbox.Infrastructure @@ -13,8 +14,9 @@ public interface IDependencyResolver : IDisposable /// /// The type of the requested instance. /// The name of the requested registration. + /// If true, the container will return with null instead of throwing . /// The resolved object. - TKey Resolve(string name = null) + TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class; /// @@ -22,8 +24,9 @@ TKey Resolve(string name = null) /// /// The type of the requested instance. /// The name of the requested registration. + /// If true, the container will return with null instead of throwing . /// The resolved object. - object Resolve(Type typeFrom, string name = null); + object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false); /// /// Resolves all registered types of a service. @@ -45,8 +48,9 @@ IEnumerable ResolveAll() /// /// The type of the requested instances. /// The name of the requested registration. + /// If true, the container will return with null instead of throwing . /// The parameter type. /// The factory delegate. - Delegate ResolveFactory(Type typeFrom, string name = null, params Type[] parameterTypes); + Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes); } } diff --git a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs index 9b686615..77c67150 100644 --- a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs +++ b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs @@ -1,5 +1,6 @@ using Stashbox.Entity; using System; +using Stashbox.Exceptions; namespace Stashbox.Infrastructure.Resolution { @@ -14,8 +15,9 @@ public interface IActivationContext /// The service type. /// The resolution scope. /// The service name. + /// If true, the container will return with null instead of throwing . /// The resolved object. - object Activate(Type type, IResolutionScope resolutionScope, string name = null); + object Activate(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false); /// /// Activates a type. @@ -33,7 +35,8 @@ public interface IActivationContext /// The parameter types. /// The resolution scope. /// The service name. + /// If true, the container will return with null instead of throwing . /// The delegate which can be used for activate a type. - Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null); + Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false); } } diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index 4d86a999..933cf632 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -90,9 +90,9 @@ public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) this.ObjectBuilder.HandlesObjectDisposal || !this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) return expr; - var method = Constants.AddDisposalMethod.MakeGenericMethod(resolveType); + var method = Constants.AddDisposalMethod.MakeGenericMethod(this.ImplementationType); - return Expression.Call(Constants.ScopeExpression, method, expr); + return Expression.Call(Constants.ScopeExpression, method, Expression.Convert(expr, this.ImplementationType)); } } } diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index e6605454..fe97731f 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -19,16 +19,16 @@ public ActivationContext(IContainerContext containerContext, IResolverSelector r this.resolverSelector = resolverSelector; } - public object Activate(Type type, IResolutionScope resolutionScope, string name = null) + public object Activate(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) { var cachedFactory = this.containerContext.DelegateRepository.GetDelegateCacheOrDefault(type, name); - return cachedFactory != null ? cachedFactory(resolutionScope) : this.Activate(ResolutionInfo.New(resolutionScope), type, name); + return cachedFactory != null ? cachedFactory(resolutionScope) : this.Activate(ResolutionInfo.New(resolutionScope, nullResultAllowed), type, name); } - public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null) + public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) { var cachedFactory = this.containerContext.DelegateRepository.GetFactoryDelegateCacheOrDefault(type, parameterTypes, name); - return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name); + return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name, nullResultAllowed); } public object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) @@ -59,9 +59,9 @@ public object Activate(ResolutionInfo resolutionInfo, Type type, string name = n return factory(resolutionInfo.ResolutionScope); } - private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null) + private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name, bool nullResultAllowed) { - var resolutionInfo = new ResolutionInfo(resolutionScope) + var resolutionInfo = new ResolutionInfo(resolutionScope, nullResultAllowed) { ParameterExpressions = parameterTypes.Length == 0 ? null : parameterTypes.Select(Expression.Parameter).ToArray() }; diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index bc0912e3..0d1f4303 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -22,12 +22,12 @@ public ResolutionScope(IActivationContext activationContext) } /// - public TKey Resolve(string name = null) where TKey : class => - this.activationContext.Activate(typeof(TKey), this, name) as TKey; + public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => + this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// - public object Resolve(Type typeFrom, string name = null) => - this.activationContext.Activate(typeFrom, this, name); + public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => + this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => @@ -41,7 +41,7 @@ public IEnumerable ResolveAll(Type typeFrom) } /// - public Delegate ResolveFactory(Type typeFrom, string name = null, params Type[] parameterTypes) => - this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name); + public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); } } diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index dc193835..04e4b004 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -1,19 +1,18 @@ using Stashbox.Utils; using System; using System.Collections.Generic; -using Stashbox.Infrastructure; namespace Stashbox { public partial class StashboxContainer { /// - public TKey Resolve(string name = null) where TKey : class => - this.ActivationContext.Activate(typeof(TKey), this, name) as TKey; + public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => + this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// - public object Resolve(Type typeFrom, string name = null) => - this.ActivationContext.Activate(typeFrom, this, name); + public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => + this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => @@ -28,7 +27,7 @@ public IEnumerable ResolveAll(Type typeFrom) } /// - public Delegate ResolveFactory(Type typeFrom, string name = null, params Type[] parameterTypes) => - this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name); + public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => + this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); } } From 2e765eb87c357e0105450398921be4eb06874d05 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Tue, 21 Mar 2017 15:53:44 +0100 Subject: [PATCH 06/27] Fixed null issue --- src/stashbox/Registration/ServiceRegistration.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index 933cf632..9c3aeca5 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -85,6 +85,9 @@ public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) var expr = this.LifetimeManager == null ? this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); + if (expr == null) + return null; + if (!this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled || this.LifetimeManager != null && this.LifetimeManager.HandlesObjectDisposal || this.ObjectBuilder.HandlesObjectDisposal || From 124798328f9e49666c8a30cbfc7bc568612ad091 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Tue, 21 Mar 2017 18:18:14 +0100 Subject: [PATCH 07/27] Fixed several issues --- src/stashbox.tests/DisposeTests.cs | 10 ++++++++++ src/stashbox.tests/GenericTests.cs | 17 +++++++++++++++++ src/stashbox.tests/LifetimeTests.cs | 2 +- src/stashbox/Lifetime/ScopedLifetime.cs | 6 ++++++ .../Registration/ServiceRegistration.cs | 3 ++- 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/stashbox.tests/DisposeTests.cs b/src/stashbox.tests/DisposeTests.cs index 5d250cbe..82c1688c 100644 --- a/src/stashbox.tests/DisposeTests.cs +++ b/src/stashbox.tests/DisposeTests.cs @@ -274,6 +274,11 @@ public class Test1 : ITest1 public void Dispose() { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Test1)); + } + this.Disposed = true; } } @@ -300,6 +305,11 @@ public class Test4 : ITest11, IDisposable public void Dispose() { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Test4)); + } + this.Disposed = true; } } diff --git a/src/stashbox.tests/GenericTests.cs b/src/stashbox.tests/GenericTests.cs index 83b31349..259a782f 100644 --- a/src/stashbox.tests/GenericTests.cs +++ b/src/stashbox.tests/GenericTests.cs @@ -38,6 +38,23 @@ public void GenericTests_Resolve_SameTime_DifferentParameter() } } + [TestMethod] + public void GenericTests_Resolve_SameTime_DifferentParameter_Singleton() + { + using (var container = new StashboxContainer()) + { + container.RegisterSingleton(typeof(ITest1<,>), typeof(Test1<,>)); + var inst = container.Resolve>(); + var inst2 = container.Resolve>(); + + Assert.IsNotNull(inst); + Assert.IsInstanceOfType(inst, typeof(Test1)); + + Assert.IsNotNull(inst2); + Assert.IsInstanceOfType(inst2, typeof(Test1)); + } + } + [TestMethod] public void GenericTests_CanResolve() { diff --git a/src/stashbox.tests/LifetimeTests.cs b/src/stashbox.tests/LifetimeTests.cs index 24dc0554..b7ec0106 100644 --- a/src/stashbox.tests/LifetimeTests.cs +++ b/src/stashbox.tests/LifetimeTests.cs @@ -80,7 +80,7 @@ public void LifetimeTests_Resolve_Parallel_Lazy() public void LifetimeTests_StateCheck() { var scoped = new ScopedLifetime(); - Assert.IsFalse(scoped.HandlesObjectDisposal); + Assert.IsTrue(scoped.HandlesObjectDisposal); Assert.IsInstanceOfType(scoped.Create(), typeof(ScopedLifetime)); var singleton = new SingletonLifetime(); diff --git a/src/stashbox/Lifetime/ScopedLifetime.cs b/src/stashbox/Lifetime/ScopedLifetime.cs index 4579e0f0..46f24ac3 100644 --- a/src/stashbox/Lifetime/ScopedLifetime.cs +++ b/src/stashbox/Lifetime/ScopedLifetime.cs @@ -49,6 +49,9 @@ public override Expression GetExpression(IContainerContext containerContext, IOb return this.expression; } + /// + public override bool HandlesObjectDisposal => true; + private static TValue GetScopedValue(IResolutionScope scope, Func factory, string lifeTimeId) { var value = scope.GetScopedItemOrDefault(lifeTimeId); @@ -56,6 +59,9 @@ private static TValue GetScopedValue(IResolutionScope scope, Func public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - var expr = this.LifetimeManager == null ? this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : + var expr = this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? + this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); if (expr == null) From 6080b64af251cc29aa25b781d8b17de6dc3fcc44 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Wed, 22 Mar 2017 00:00:51 +0100 Subject: [PATCH 08/27] Refactor --- src/stashbox/Constants.cs | 2 +- .../Infrastructure/IDependencyResolver.cs | 11 +++++++++++ .../Infrastructure/IDisposableHandler.cs | 19 +++++++++++++++++++ .../Infrastructure/IResolutionScope.cs | 11 +---------- .../Infrastructure/IStashboxContainer.cs | 10 ---------- src/stashbox/ResolutionScope.cs | 18 +++++++++++------- 6 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 src/stashbox/Infrastructure/IDisposableHandler.cs diff --git a/src/stashbox/Constants.cs b/src/stashbox/Constants.cs index 3ff866a2..f34e8534 100644 --- a/src/stashbox/Constants.cs +++ b/src/stashbox/Constants.cs @@ -11,7 +11,7 @@ internal static class Constants { public static ParameterExpression ScopeExpression = Expression.Parameter(typeof(IResolutionScope)); - public static MethodInfo AddDisposalMethod = typeof(IResolutionScope).GetSingleMethod("AddDisposableTracking"); + public static MethodInfo AddDisposalMethod = typeof(IDisposableHandler).GetSingleMethod("AddDisposableTracking"); public static MethodInfo BuildExtensionMethod = typeof(IContainerExtensionManager).GetSingleMethod("ExecutePostBuildExtensions"); diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index 665db3fc..d52123b5 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -1,6 +1,7 @@ using Stashbox.Exceptions; using System; using System.Collections.Generic; +using Stashbox.Infrastructure.Resolution; namespace Stashbox.Infrastructure { @@ -9,6 +10,11 @@ namespace Stashbox.Infrastructure /// public interface IDependencyResolver : IDisposable { + /// + /// The activation context. + /// + IActivationContext ActivationContext { get; } + /// /// Resolves an instance from the container. /// @@ -52,5 +58,10 @@ IEnumerable ResolveAll() /// The parameter type. /// The factory delegate. Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes); + + /// + /// Begins a new scope. + /// + IDependencyResolver BeginScope(); } } diff --git a/src/stashbox/Infrastructure/IDisposableHandler.cs b/src/stashbox/Infrastructure/IDisposableHandler.cs new file mode 100644 index 00000000..3253866f --- /dev/null +++ b/src/stashbox/Infrastructure/IDisposableHandler.cs @@ -0,0 +1,19 @@ +using System; + +namespace Stashbox.Infrastructure +{ + /// + /// Represents a disposable handler. + /// + public interface IDisposableHandler : IDisposable + { + /// + /// Adds a service for further disposable tracking. + /// + /// The type parameter. + /// The object. + /// The object. + TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable; + } +} diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs index 83deb9c1..c99fee93 100644 --- a/src/stashbox/Infrastructure/IResolutionScope.cs +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -5,17 +5,8 @@ namespace Stashbox.Infrastructure /// /// Represents a resolution scope. /// - public interface IResolutionScope : IDisposable + public interface IResolutionScope : IDisposableHandler { - /// - /// Adds a service for further disposable tracking. - /// - /// The type parameter. - /// The object. - /// The object. - TDisposable AddDisposableTracking(TDisposable disposable) - where TDisposable : IDisposable; - /// /// Adds or updates an item in the scope. /// diff --git a/src/stashbox/Infrastructure/IStashboxContainer.cs b/src/stashbox/Infrastructure/IStashboxContainer.cs index 5e6b0309..485e0d28 100644 --- a/src/stashbox/Infrastructure/IStashboxContainer.cs +++ b/src/stashbox/Infrastructure/IStashboxContainer.cs @@ -21,21 +21,11 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// The resolver. void RegisterResolver(Resolver resolver); - /// - /// Begins a new scope. - /// - IDependencyResolver BeginScope(); - /// /// Creates a child container. /// IStashboxContainer CreateChildContainer(); - /// - /// The activation context. - /// - IActivationContext ActivationContext { get; } - /// /// Stores the parent container object if has any, otherwise null. /// diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index 0d1f4303..ee5206fc 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -10,7 +10,8 @@ namespace Stashbox /// public class ResolutionScope : ResolutionScopeBase, IDependencyResolver { - private readonly IActivationContext activationContext; + /// + public IActivationContext ActivationContext { get; } /// /// Constructs a resolution scope. @@ -18,30 +19,33 @@ public class ResolutionScope : ResolutionScopeBase, IDependencyResolver /// The dependency resolver. public ResolutionScope(IActivationContext activationContext) { - this.activationContext = activationContext; + this.ActivationContext = activationContext; } /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; + this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); + this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.activationContext.Activate(type, this); + return (IEnumerable)this.ActivationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + + /// + public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); } } From f1c5c0ff66e41f2f46392e4a70b9845155970e2c Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Wed, 22 Mar 2017 01:17:11 +0100 Subject: [PATCH 09/27] Small refactor --- src/stashbox.tests/EnumerableTests.cs | 10 ++++++++++ src/stashbox/Constants.cs | 18 ++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/stashbox.tests/EnumerableTests.cs b/src/stashbox.tests/EnumerableTests.cs index ba342848..519af9a2 100644 --- a/src/stashbox.tests/EnumerableTests.cs +++ b/src/stashbox.tests/EnumerableTests.cs @@ -168,6 +168,16 @@ public void EnumerableTests_Resolve_Func() Assert.AreEqual(3, all.Count()); } + [TestMethod] + public void EnumerableTests_ResolveAll_Empty() + { + IStashboxContainer container = new StashboxContainer(); + + var all = container.ResolveAll(); + + Assert.AreEqual(0, all.Count()); + } + [TestMethod] public void EnumerableTests_ResolveNonGeneric() { diff --git a/src/stashbox/Constants.cs b/src/stashbox/Constants.cs index f34e8534..c758c5f4 100644 --- a/src/stashbox/Constants.cs +++ b/src/stashbox/Constants.cs @@ -7,18 +7,24 @@ namespace Stashbox { - internal static class Constants + /// + /// Holds the constant values used by the conainer. + /// + public static class Constants { + /// + /// The scope parameter expression. + /// public static ParameterExpression ScopeExpression = Expression.Parameter(typeof(IResolutionScope)); - public static MethodInfo AddDisposalMethod = typeof(IDisposableHandler).GetSingleMethod("AddDisposableTracking"); + internal static MethodInfo AddDisposalMethod = typeof(IDisposableHandler).GetSingleMethod("AddDisposableTracking"); - public static MethodInfo BuildExtensionMethod = typeof(IContainerExtensionManager).GetSingleMethod("ExecutePostBuildExtensions"); + internal static MethodInfo BuildExtensionMethod = typeof(IContainerExtensionManager).GetSingleMethod("ExecutePostBuildExtensions"); - public static MethodInfo GetScopedValueMethod = typeof(ScopedLifetime).GetSingleMethod("GetScopedValue", true); + internal static MethodInfo GetScopedValueMethod = typeof(ScopedLifetime).GetSingleMethod("GetScopedValue", true); - public static Type DisposableType = typeof(IDisposable); + internal static Type DisposableType = typeof(IDisposable); - public static Type FuncType = typeof(Func<>); + internal static Type FuncType = typeof(Func<>); } } From a57c3cf9d641fa7a5a9069cc18308bdfa1fb80b5 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Wed, 22 Mar 2017 01:30:56 +0100 Subject: [PATCH 10/27] Few fixes --- src/stashbox/Constants.cs | 2 +- .../Infrastructure/IDisposableHandler.cs | 19 ------------------- .../Infrastructure/IResolutionScope.cs | 11 ++++++++++- 3 files changed, 11 insertions(+), 21 deletions(-) delete mode 100644 src/stashbox/Infrastructure/IDisposableHandler.cs diff --git a/src/stashbox/Constants.cs b/src/stashbox/Constants.cs index c758c5f4..25a387de 100644 --- a/src/stashbox/Constants.cs +++ b/src/stashbox/Constants.cs @@ -17,7 +17,7 @@ public static class Constants /// public static ParameterExpression ScopeExpression = Expression.Parameter(typeof(IResolutionScope)); - internal static MethodInfo AddDisposalMethod = typeof(IDisposableHandler).GetSingleMethod("AddDisposableTracking"); + internal static MethodInfo AddDisposalMethod = typeof(IResolutionScope).GetSingleMethod("AddDisposableTracking"); internal static MethodInfo BuildExtensionMethod = typeof(IContainerExtensionManager).GetSingleMethod("ExecutePostBuildExtensions"); diff --git a/src/stashbox/Infrastructure/IDisposableHandler.cs b/src/stashbox/Infrastructure/IDisposableHandler.cs deleted file mode 100644 index 3253866f..00000000 --- a/src/stashbox/Infrastructure/IDisposableHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Stashbox.Infrastructure -{ - /// - /// Represents a disposable handler. - /// - public interface IDisposableHandler : IDisposable - { - /// - /// Adds a service for further disposable tracking. - /// - /// The type parameter. - /// The object. - /// The object. - TDisposable AddDisposableTracking(TDisposable disposable) - where TDisposable : IDisposable; - } -} diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs index c99fee93..798b697b 100644 --- a/src/stashbox/Infrastructure/IResolutionScope.cs +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -5,7 +5,7 @@ namespace Stashbox.Infrastructure /// /// Represents a resolution scope. /// - public interface IResolutionScope : IDisposableHandler + public interface IResolutionScope : IDisposable { /// /// Adds or updates an item in the scope. @@ -20,5 +20,14 @@ public interface IResolutionScope : IDisposableHandler /// The key. /// The item or null if it doesn't exists. object GetScopedItemOrDefault(object key); + + /// + /// Adds a service for further disposable tracking. + /// + /// The type parameter. + /// The object. + /// The object. + TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable; } } From 3cd31f5aaf31b4ae311a97df8adab5a0c63c5a72 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Wed, 22 Mar 2017 01:49:05 +0100 Subject: [PATCH 11/27] Added more unit tests --- src/stashbox.tests/EnumerableTests.cs | 74 +++++++++++++++++-- src/stashbox.tests/FuncTests.cs | 39 +++++++++- src/stashbox.tests/LazyTests.cs | 37 ++++++++++ .../BuildUp/Resolution/LazyResolver.cs | 3 +- 4 files changed, 143 insertions(+), 10 deletions(-) diff --git a/src/stashbox.tests/EnumerableTests.cs b/src/stashbox.tests/EnumerableTests.cs index 519af9a2..f4ab3806 100644 --- a/src/stashbox.tests/EnumerableTests.cs +++ b/src/stashbox.tests/EnumerableTests.cs @@ -22,7 +22,7 @@ public void EnumerableTests_Resolve_Array_PreserveOrder() var all = container.Resolve(); - Assert.AreEqual(3, all.Count()); + Assert.AreEqual(3, all.Length); } [TestMethod] @@ -35,7 +35,7 @@ public void EnumerableTests_Resolve_IList() var all = container.Resolve>(); - Assert.AreEqual(3, all.Count()); + Assert.AreEqual(3, all.Count); } [TestMethod] @@ -48,7 +48,7 @@ public void EnumerableTests_Resolve_ICollection() var all = container.Resolve>(); - Assert.AreEqual(3, all.Count()); + Assert.AreEqual(3, all.Count); } [TestMethod] @@ -61,7 +61,7 @@ public void EnumerableTests_Resolve_IReadonlyCollection() var all = container.Resolve>(); - Assert.AreEqual(3, all.Count()); + Assert.AreEqual(3, all.Count); } [TestMethod] @@ -74,7 +74,7 @@ public void EnumerableTests_Resolve_IReadOnlyList() var all = container.Resolve>(); - Assert.AreEqual(3, all.Count()); + Assert.AreEqual(3, all.Count); } [TestMethod] @@ -97,6 +97,18 @@ public void EnumerableTests_Resolve() Assert.AreEqual(2, all2.Count()); } + [TestMethod] + public void EnumerableTests_Resolve_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var all = container.Resolve>(); + var all2 = container.ResolveAll(); + + Assert.AreEqual(0, all.Count()); + Assert.AreEqual(0, all2.Count()); + } + [TestMethod] public void EnumerableTests_Resolve_Parent() { @@ -112,6 +124,18 @@ public void EnumerableTests_Resolve_Parent() Assert.AreEqual(3, all.Count()); } + [TestMethod] + public void EnumerableTests_Resolve_Parent_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var child = container.BeginScope(); + + var all = child.Resolve>(); + + Assert.AreEqual(0, all.Count()); + } + [TestMethod] public void EnumerableTests_Resolve_Parent_Lazy() { @@ -127,6 +151,18 @@ public void EnumerableTests_Resolve_Parent_Lazy() Assert.AreEqual(3, all.Count()); } + [TestMethod] + public void EnumerableTests_Resolve_Parent_Lazy_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var child = container.BeginScope(); + + var all = child.Resolve>>(); + + Assert.AreEqual(0, all.Count()); + } + [TestMethod] public void EnumerableTests_Resolve_Parent_Func() { @@ -142,6 +178,18 @@ public void EnumerableTests_Resolve_Parent_Func() Assert.AreEqual(3, all.Count()); } + [TestMethod] + public void EnumerableTests_Resolve_Parent_Func_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var child = container.BeginScope(); + + var all = child.Resolve>>(); + + Assert.AreEqual(0, all.Count()); + } + [TestMethod] public void EnumerableTests_Resolve_Lazy() { @@ -155,6 +203,16 @@ public void EnumerableTests_Resolve_Lazy() Assert.AreEqual(3, all.Count()); } + [TestMethod] + public void EnumerableTests_Resolve_Lazy_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var all = container.Resolve>>(); + + Assert.AreEqual(0, all.Count()); + } + [TestMethod] public void EnumerableTests_Resolve_Func() { @@ -169,11 +227,11 @@ public void EnumerableTests_Resolve_Func() } [TestMethod] - public void EnumerableTests_ResolveAll_Empty() + public void EnumerableTests_Resolve_Func_Null() { IStashboxContainer container = new StashboxContainer(); - - var all = container.ResolveAll(); + + var all = container.Resolve>>(); Assert.AreEqual(0, all.Count()); } diff --git a/src/stashbox.tests/FuncTests.cs b/src/stashbox.tests/FuncTests.cs index 4d99056d..a1436fa2 100644 --- a/src/stashbox.tests/FuncTests.cs +++ b/src/stashbox.tests/FuncTests.cs @@ -21,6 +21,15 @@ public void FuncTests_Resolve() Assert.IsInstanceOfType(inst(), typeof(Test)); } + [TestMethod] + public void FuncTests_Resolve_Null() + { + var container = new StashboxContainer(); + var inst = container.Resolve>(nullResultAllowed: true); + + Assert.IsNull(inst); + } + [TestMethod] public void FuncTests_Resolve_Lazy() { @@ -33,6 +42,15 @@ public void FuncTests_Resolve_Lazy() Assert.IsInstanceOfType(inst().Value, typeof(Test)); } + [TestMethod] + public void FuncTests_Resolve_Lazy_Null() + { + var container = new StashboxContainer(); + var inst = container.Resolve>>(nullResultAllowed: true); + + Assert.IsNull(inst); + } + [TestMethod] public void FuncTests_Resolve_Enumerable() { @@ -45,6 +63,15 @@ public void FuncTests_Resolve_Enumerable() Assert.IsInstanceOfType(inst().First(), typeof(Test)); } + [TestMethod] + public void FuncTests_Resolve_Enumerable_Null() + { + var container = new StashboxContainer(); + var inst = container.Resolve>>(); + + Assert.AreEqual(0, inst().Count()); + } + [TestMethod] public void FuncTests_Resolve_ConstructorDependency() { @@ -58,6 +85,16 @@ public void FuncTests_Resolve_ConstructorDependency() Assert.IsInstanceOfType(inst.Test(), typeof(Test)); } + [TestMethod] + public void FuncTests_Resolve_ConstructorDependency_Null() + { + var container = new StashboxContainer(); + container.RegisterType(); + var inst = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(inst); + } + [TestMethod] public void FuncTests_Resolve_ParameterInjection() { @@ -147,7 +184,7 @@ public void FuncTests_Resolve_ParameterInjection_Mixed2() container.RegisterType(); container.RegisterType(); var inst = container.Resolve(); - + var d3 = new Dep3(); var d = inst.Dep(d3); diff --git a/src/stashbox.tests/LazyTests.cs b/src/stashbox.tests/LazyTests.cs index 4f99cf00..8858837c 100644 --- a/src/stashbox.tests/LazyTests.cs +++ b/src/stashbox.tests/LazyTests.cs @@ -21,6 +21,15 @@ public void LazyTests_Resolve() Assert.IsInstanceOfType(inst.Value, typeof(Test)); } + [TestMethod] + public void LazyTests_Resolve_Null() + { + var container = new StashboxContainer(); + var inst = container.Resolve>(nullResultAllowed: true); + + Assert.IsNull(inst); + } + [TestMethod] public void LazyTests_Resolve_Func() { @@ -34,6 +43,15 @@ public void LazyTests_Resolve_Func() Assert.IsInstanceOfType(inst.Value(), typeof(Test)); } + [TestMethod] + public void LazyTests_Resolve_Func_Null() + { + var container = new StashboxContainer(); + var inst = container.Resolve>>(nullResultAllowed: true); + + Assert.IsNull(inst); + } + [TestMethod] public void LazyTests_Resolve_Enumerable() { @@ -47,6 +65,15 @@ public void LazyTests_Resolve_Enumerable() Assert.IsInstanceOfType(inst.Value.First(), typeof(Test)); } + [TestMethod] + public void LazyTests_Resolve_Enumerable_Null() + { + var container = new StashboxContainer(); + var inst = container.Resolve>>(); + + Assert.AreEqual(0, inst.Value.Count()); + } + [TestMethod] public void LazyTests_Resolve_ConstructorDependency() { @@ -61,6 +88,16 @@ public void LazyTests_Resolve_ConstructorDependency() Assert.IsInstanceOfType(inst.Test.Value, typeof(Test)); } + [TestMethod] + public void LazyTests_Resolve_ConstructorDependency_Null() + { + var container = new StashboxContainer(); + container.RegisterType(); + var inst = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(inst); + } + [TestMethod] public void LazyTests_Resolve_Circular() { diff --git a/src/stashbox/BuildUp/Resolution/LazyResolver.cs b/src/stashbox/BuildUp/Resolution/LazyResolver.cs index f369379c..8d976404 100644 --- a/src/stashbox/BuildUp/Resolution/LazyResolver.cs +++ b/src/stashbox/BuildUp/Resolution/LazyResolver.cs @@ -43,7 +43,8 @@ public override Expression GetExpression(IContainerContext containerContext, Typ this.CreateLazyExpressionCall(registration, lazyArgumentInfo.Type, lazyConstructor, resolutionInfo); var expression = this.resolverSelector.GetResolverExpression(containerContext, lazyArgumentInfo, resolutionInfo); - return Expression.New(lazyConstructor, Expression.Lambda(expression)); + + return expression == null ? null : Expression.New(lazyConstructor, Expression.Lambda(expression)); } public override Expression[] GetExpressions(IContainerContext containerContext, TypeInformation typeInfo, ResolutionInfo resolutionInfo) From 4fd147206234ad4fdc8e5502732aaa7b7c786cb5 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Wed, 22 Mar 2017 15:23:39 +0100 Subject: [PATCH 12/27] Moved some apis to public scope --- src/stashbox/Extensions/ExpressionExtensions.cs | 17 ++++++++++++++++- src/stashbox/Lifetime/ScopedLifetime.cs | 12 ++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/stashbox/Extensions/ExpressionExtensions.cs b/src/stashbox/Extensions/ExpressionExtensions.cs index 87f87f41..f1a15997 100644 --- a/src/stashbox/Extensions/ExpressionExtensions.cs +++ b/src/stashbox/Extensions/ExpressionExtensions.cs @@ -6,8 +6,17 @@ namespace System.Linq.Expressions { - internal static class ExpressionExtensions + /// + /// Holds the extension methods. + /// + public static class ExpressionExtensions { + /// + /// Compiles an to a of , . + /// + /// The expression. + /// The scope parameter expression. + /// The compiled delegate. public static Func CompileDelegate(this Expression expression, ParameterExpression scopeParameter) { if (expression.NodeType == ExpressionType.Constant) @@ -26,6 +35,12 @@ public static Func CompileDelegate(this Expression exp #endif } + /// + /// Compiles an to a of , . + /// + /// The expression. + /// The scope parameter expression. + /// The compiled delegate. public static Func CompileDelegate(this LambdaExpression expression, ParameterExpression scopeParameter) { #if NET45 || NET40 diff --git a/src/stashbox/Lifetime/ScopedLifetime.cs b/src/stashbox/Lifetime/ScopedLifetime.cs index 46f24ac3..c5cfaf0e 100644 --- a/src/stashbox/Lifetime/ScopedLifetime.cs +++ b/src/stashbox/Lifetime/ScopedLifetime.cs @@ -12,14 +12,14 @@ public class ScopedLifetime : LifetimeBase { private volatile Expression expression; private readonly object syncObject = new object(); - private readonly string id; + private readonly string scopeId; /// /// Constructs a . /// public ScopedLifetime() { - this.id = Guid.NewGuid().ToString(); + this.scopeId = Guid.NewGuid().ToString(); } /// @@ -43,7 +43,7 @@ public override Expression GetExpression(IContainerContext containerContext, IOb this.expression = Expression.Call(method, Constants.ScopeExpression, - Expression.Constant(factory), Expression.Constant(this.id)); + Expression.Constant(factory), Expression.Constant(this.scopeId)); } return this.expression; @@ -52,13 +52,13 @@ public override Expression GetExpression(IContainerContext containerContext, IOb /// public override bool HandlesObjectDisposal => true; - private static TValue GetScopedValue(IResolutionScope scope, Func factory, string lifeTimeId) + private static TValue GetScopedValue(IResolutionScope scope, Func factory, string scopeId) { - var value = scope.GetScopedItemOrDefault(lifeTimeId); + var value = scope.GetScopedItemOrDefault(scopeId); if(value == null) { value = factory(scope); - scope.AddOrUpdateScopedItem(lifeTimeId, value); + scope.AddOrUpdateScopedItem(scopeId, value); if (value is IDisposable disposable) scope.AddDisposableTracking(disposable); From 2b326eec74244fd15e84aa3a58efab1704e6e4e4 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Thu, 23 Mar 2017 12:53:58 +0100 Subject: [PATCH 13/27] Refactor --- src/stashbox.tests/AttributeTests.cs | 30 --------- .../Infrastructure/IDependencyResolver.cs | 13 +--- .../Infrastructure/IResolutionScope.cs | 8 ++- .../Infrastructure/IStashboxContainer.cs | 13 +++- .../Resolution/IActivationContext.cs | 9 --- src/stashbox/Resolution/ActivationContext.cs | 2 +- src/stashbox/ResolutionScope.cs | 63 +++++++++++++++---- src/stashbox/ResolutionScopeBase.cs | 62 ------------------ src/stashbox/StashboxContainer.Resolver.cs | 10 +-- src/stashbox/StashboxContainer.cs | 31 +++++---- 10 files changed, 97 insertions(+), 144 deletions(-) delete mode 100644 src/stashbox/ResolutionScopeBase.cs diff --git a/src/stashbox.tests/AttributeTests.cs b/src/stashbox.tests/AttributeTests.cs index 90d8276a..1d75f458 100644 --- a/src/stashbox.tests/AttributeTests.cs +++ b/src/stashbox.tests/AttributeTests.cs @@ -2,10 +2,7 @@ using Stashbox.Attributes; using Stashbox.Utils; using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; -using Stashbox.Entity; namespace Stashbox.Tests { @@ -41,33 +38,6 @@ public void AttributeTests_Resolve() Assert.IsInstanceOfType(test3.test2, typeof(Test22)); } - [TestMethod] - public void AttributeTests_Resolve_Activator() - { - var container = new StashboxContainer(); - container.RegisterType("test1"); - container.RegisterType("test11"); - container.RegisterType("test12"); - - var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(ITest1), "test12"); - Assert.IsNotNull(inst); - Assert.IsInstanceOfType(inst, typeof(Test12)); - } - - [TestMethod] - public void AttributeTests_Resolve_Activator_Resolver() - { - var container = new StashboxContainer(); - container.RegisterType("test1"); - container.RegisterType("test11"); - container.RegisterType("test12"); - - var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(IEnumerable)); - Assert.IsNotNull(inst); - Assert.IsInstanceOfType(inst, typeof(IEnumerable)); - Assert.AreEqual(3, ((IEnumerable)inst).Count()); - } - [TestMethod] public void AttributeTests_Named_Resolution() { diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index d52123b5..d038f0ae 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -1,20 +1,14 @@ using Stashbox.Exceptions; using System; using System.Collections.Generic; -using Stashbox.Infrastructure.Resolution; namespace Stashbox.Infrastructure { /// /// Represents a dependency resolver. /// - public interface IDependencyResolver : IDisposable + public interface IDependencyResolver { - /// - /// The activation context. - /// - IActivationContext ActivationContext { get; } - /// /// Resolves an instance from the container. /// @@ -58,10 +52,5 @@ IEnumerable ResolveAll() /// The parameter type. /// The factory delegate. Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes); - - /// - /// Begins a new scope. - /// - IDependencyResolver BeginScope(); } } diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs index 798b697b..22236ce3 100644 --- a/src/stashbox/Infrastructure/IResolutionScope.cs +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -5,7 +5,7 @@ namespace Stashbox.Infrastructure /// /// Represents a resolution scope. /// - public interface IResolutionScope : IDisposable + public interface IResolutionScope : IDependencyResolver, IDisposable { /// /// Adds or updates an item in the scope. @@ -29,5 +29,11 @@ public interface IResolutionScope : IDisposable /// The object. TDisposable AddDisposableTracking(TDisposable disposable) where TDisposable : IDisposable; + + /// + /// Begins a new scope. + /// + /// The new scope. + IResolutionScope BeginScope(); } } diff --git a/src/stashbox/Infrastructure/IStashboxContainer.cs b/src/stashbox/Infrastructure/IStashboxContainer.cs index 485e0d28..cc398dc9 100644 --- a/src/stashbox/Infrastructure/IStashboxContainer.cs +++ b/src/stashbox/Infrastructure/IStashboxContainer.cs @@ -7,7 +7,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency injection container. /// - public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator + public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator, IDisposable { /// /// Registers a into the container. @@ -36,6 +36,11 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// IContainerContext ContainerContext { get; } + /// + /// The activation context. + /// + IResolutionScope RootScope { get; } + /// /// Checks a type can be resolved by the container. /// @@ -52,6 +57,12 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// True if the service can be resolved, otherwise false. bool CanResolve(Type typeFrom, string name = null); + /// + /// Begins a new scope. + /// + /// The new scope. + IResolutionScope BeginScope(); + /// /// Builds up an instance, the container will perform injections and extensions on it. /// diff --git a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs index 77c67150..022856c8 100644 --- a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs +++ b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs @@ -19,15 +19,6 @@ public interface IActivationContext /// The resolved object. object Activate(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false); - /// - /// Activates a type. - /// - /// The resolution info. - /// The type. - /// The service name. - /// The resolved object. - object Activate(ResolutionInfo resolutionInfo, Type type, string name = null); - /// /// Activates a type via a delegate. /// diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index fe97731f..c18630e6 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -31,7 +31,7 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name, nullResultAllowed); } - public object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) + private object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) { var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index ee5206fc..cac7fa9c 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -2,50 +2,89 @@ using System.Collections.Generic; using Stashbox.Infrastructure; using Stashbox.Infrastructure.Resolution; +using Stashbox.Utils; namespace Stashbox { /// /// Represents a resolution scope. /// - public class ResolutionScope : ResolutionScopeBase, IDependencyResolver + public class ResolutionScope : IResolutionScope { - /// - public IActivationContext ActivationContext { get; } + private readonly IActivationContext activationContext; + private readonly AtomicBool disposed; + private readonly ConcurrentStore disposableObjects; + private readonly ConcurrentTree scopedItems; /// - /// Constructs a resolution scope. + /// Constructs a . /// - /// The dependency resolver. public ResolutionScope(IActivationContext activationContext) { - this.ActivationContext = activationContext; + this.activationContext = activationContext; + this.disposed = new AtomicBool(); + this.disposableObjects = new ConcurrentStore(); + this.scopedItems = new ConcurrentTree(); + } + + /// + public TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable + { + this.disposableObjects.Add(disposable); + return disposable; } + /// + public void AddOrUpdateScopedItem(object key, object value) => + this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); + + /// + public object GetScopedItemOrDefault(object key) => + this.scopedItems.GetOrDefault(key); + /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; + this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); + this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.ActivationContext.Activate(type, this); + return (IEnumerable)this.activationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); /// - public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); + public IResolutionScope BeginScope() => new ResolutionScope(this.activationContext); + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the scope. + /// + /// Indicates the scope is disposing or not. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed.CompareExchange(false, true) || !disposing) return; + foreach (var disposableObject in disposableObjects) + disposableObject?.Dispose(); + } } } diff --git a/src/stashbox/ResolutionScopeBase.cs b/src/stashbox/ResolutionScopeBase.cs deleted file mode 100644 index 1e5d9ab4..00000000 --- a/src/stashbox/ResolutionScopeBase.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using Stashbox.Infrastructure; -using Stashbox.Utils; - -namespace Stashbox -{ - /// - /// Represents a base resolution scope. - /// - public class ResolutionScopeBase : IResolutionScope - { - private readonly AtomicBool disposed; - - private readonly ConcurrentStore disposableObjects; - - private readonly ConcurrentTree scopedItems; - - /// - /// Constructs a . - /// - public ResolutionScopeBase() - { - this.disposed = new AtomicBool(); - this.disposableObjects = new ConcurrentStore(); - this.scopedItems = new ConcurrentTree(); - } - - /// - public TDisposable AddDisposableTracking(TDisposable disposable) - where TDisposable : IDisposable - { - this.disposableObjects.Add(disposable); - return disposable; - } - - /// - public void AddOrUpdateScopedItem(object key, object value) => - this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); - - /// - public object GetScopedItemOrDefault(object key) => - this.scopedItems.GetOrDefault(key); - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes the scope. - /// - /// Indicates the scope is disposing or not. - protected virtual void Dispose(bool disposing) - { - if (!this.disposed.CompareExchange(false, true) || !disposing) return; - foreach (var disposableObject in disposableObjects) - disposableObject?.Dispose(); - } - } -} diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index 04e4b004..ea2114f3 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -8,26 +8,26 @@ public partial class StashboxContainer { /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; + this.activationContext.Activate(typeof(TKey), this.RootScope, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); + this.activationContext.Activate(typeFrom, this.RootScope, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + this.activationContext.Activate(typeof(IEnumerable), this.RootScope) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { Shield.EnsureNotNull(typeFrom, nameof(typeFrom)); var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.ActivationContext.Activate(type, this); + return (IEnumerable)this.activationContext.Activate(type, this.RootScope); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this.RootScope, name, nullResultAllowed); } } diff --git a/src/stashbox/StashboxContainer.cs b/src/stashbox/StashboxContainer.cs index d209208d..1d5b5ea8 100644 --- a/src/stashbox/StashboxContainer.cs +++ b/src/stashbox/StashboxContainer.cs @@ -19,12 +19,13 @@ namespace Stashbox /// /// Represents the stashbox dependency injection container. /// - public partial class StashboxContainer : ResolutionScopeBase, IStashboxContainer + public partial class StashboxContainer : IStashboxContainer { private readonly IContainerExtensionManager containerExtensionManager; private readonly IResolverSelector resolverSelector; private readonly IRegistrationRepository registrationRepository; private readonly IExpressionBuilder expressionBuilder; + private readonly IActivationContext activationContext; private readonly AtomicBool disposed; /// @@ -43,8 +44,8 @@ public StashboxContainer(Action config = null) this.registrationRepository = new RegistrationRepository(configurator); this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), configurator, new DecoratorRepository()); - this.ActivationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); - + this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.RootScope = new ResolutionScope(this.activationContext); this.RegisterResolvers(); } @@ -59,7 +60,8 @@ internal StashboxContainer(IStashboxContainer parentContainer, IContainerExtensi this.registrationRepository = new RegistrationRepository(parentContainer.ContainerContext.ContainerConfigurator); this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), parentContainer.ContainerContext.ContainerConfigurator, parentContainer.ContainerContext.DecoratorRepository); - this.ActivationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.RootScope = new ResolutionScope(this.activationContext); this.containerExtensionManager.ReinitalizeExtensions(this.ContainerContext); } @@ -88,7 +90,7 @@ public void Validate() { foreach (var serviceRegistration in this.registrationRepository.GetAllRegistrations()) { - var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this), serviceRegistration.ServiceType); + var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this.RootScope), serviceRegistration.ServiceType); if (expression == null) throw new ResolutionFailedException(serviceRegistration.ImplementationType.FullName); } @@ -101,14 +103,14 @@ public void Validate() public IContainerContext ContainerContext { get; } /// - public IActivationContext ActivationContext { get; } + public IResolutionScope RootScope { get; } /// public IStashboxContainer CreateChildContainer() => new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector, this.expressionBuilder); /// - public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); + public IResolutionScope BeginScope() => this.RootScope.BeginScope(); /// public void Configure(Action config) => @@ -131,23 +133,30 @@ public TTo BuildUp(TTo instance) where TTo : class var typeTo = instance.GetType(); var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, typeTo); - var resolutionInfo = ResolutionInfo.New(this); + var resolutionInfo = ResolutionInfo.New(this.RootScope); var expr = this.expressionBuilder.CreateFillExpression(this.containerExtensionManager, this.ContainerContext, Expression.Constant(instance), resolutionInfo, typeTo, null, metaInfoProvider.GetResolutionMembers(resolutionInfo), metaInfoProvider.GetResolutionMethods(resolutionInfo)); var factory = expr.CompileDelegate(Constants.ScopeExpression); - return factory(this) as TTo; + return factory(this.RootScope) as TTo; + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); } /// /// Disposes the container. /// /// Indicates the container is disposing or not. - protected override void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (!this.disposed.CompareExchange(false, true) || !disposing) return; - base.Dispose(true); + this.RootScope.Dispose(); this.registrationRepository.CleanUp(); this.containerExtensionManager.CleanUp(); } From d94fa7ae36cadcf8993692c1bdba57569212ad56 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Thu, 23 Mar 2017 18:06:15 +0100 Subject: [PATCH 14/27] Small refactor --- src/stashbox/Constants.cs | 4 ++++ src/stashbox/Resolution/ActivationContext.cs | 22 +++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/stashbox/Constants.cs b/src/stashbox/Constants.cs index 25a387de..3ab365be 100644 --- a/src/stashbox/Constants.cs +++ b/src/stashbox/Constants.cs @@ -26,5 +26,9 @@ public static class Constants internal static Type DisposableType = typeof(IDisposable); internal static Type FuncType = typeof(Func<>); + + internal static Type ResolutionScopeType = typeof(IResolutionScope); + + internal static Type StashboxContainerType = typeof(IStashboxContainer); } } diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index c18630e6..8f9ef88c 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -22,7 +22,7 @@ public ActivationContext(IContainerContext containerContext, IResolverSelector r public object Activate(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) { var cachedFactory = this.containerContext.DelegateRepository.GetDelegateCacheOrDefault(type, name); - return cachedFactory != null ? cachedFactory(resolutionScope) : this.Activate(ResolutionInfo.New(resolutionScope, nullResultAllowed), type, name); + return cachedFactory != null ? cachedFactory(resolutionScope) : this.ActivateType(type, resolutionScope, name, nullResultAllowed); } public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) @@ -31,32 +31,38 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name, nullResultAllowed); } - private object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) + private object ActivateType(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) { + if (type == Constants.ResolutionScopeType) + return resolutionScope; + + if (type == Constants.StashboxContainerType) + return this.containerContext.Container; + var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) { - var ragistrationFactory = registration.GetExpression(resolutionInfo, type)?.CompileDelegate(Constants.ScopeExpression); + var ragistrationFactory = registration.GetExpression(ResolutionInfo.New(resolutionScope, nullResultAllowed), type)?.CompileDelegate(Constants.ScopeExpression); if (ragistrationFactory == null) - if (resolutionInfo.NullResultAllowed) + if (nullResultAllowed) return null; else throw new ResolutionFailedException(type.FullName); this.containerContext.DelegateRepository.AddServiceDelegate(type, ragistrationFactory, name); - return ragistrationFactory(resolutionInfo.ResolutionScope); + return ragistrationFactory(resolutionScope); } - var expr = this.resolverSelector.GetResolverExpression(containerContext, new TypeInformation { Type = type, DependencyName = name }, resolutionInfo); + var expr = this.resolverSelector.GetResolverExpression(containerContext, new TypeInformation { Type = type, DependencyName = name }, ResolutionInfo.New(resolutionScope, nullResultAllowed)); if (expr == null) - if (resolutionInfo.NullResultAllowed) + if (nullResultAllowed) return null; else throw new ResolutionFailedException(type.FullName); var factory = expr.CompileDelegate(Constants.ScopeExpression); this.containerContext.DelegateRepository.AddServiceDelegate(type, factory, name); - return factory(resolutionInfo.ResolutionScope); + return factory(resolutionScope); } private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name, bool nullResultAllowed) From 95de48d9face410af9ab2fc151a8fbd2b4d78e86 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Thu, 23 Mar 2017 18:43:02 +0100 Subject: [PATCH 15/27] Few fixes --- src/stashbox.tests/ContainerTests.cs | 108 ++++++++++++++++++ src/stashbox/Resolution/ResolutionStrategy.cs | 6 + 2 files changed, 114 insertions(+) diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index 39282c33..c27184bf 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -127,6 +127,102 @@ public void ContainerTests_ResolverTest_SupportsMany() Assert.IsInstanceOfType(inst.First(), typeof(Test1)); } + [TestMethod] + public void ContainerTests_ResolutionScopeResolve() + { + var container = new StashboxContainer(); + var scope = container.Resolve(); + + Assert.IsNotNull(scope); + Assert.AreSame(container.RootScope, scope); + } + + [TestMethod] + public void ContainerTests_ResolutionScopeResolve_BeginScope() + { + var container = new StashboxContainer(); + var newScope = container.BeginScope(); + var scope = newScope.Resolve(); + + Assert.IsNotNull(scope); + Assert.AreSame(newScope, scope); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve() + { + var container = new StashboxContainer(); + var self = container.Resolve(); + + Assert.IsNotNull(self); + Assert.AreSame(container, self); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_BeginScope() + { + var container = new StashboxContainer(); + var newScope = container.BeginScope(); + var self = newScope.Resolve(); + + Assert.IsNotNull(self); + Assert.AreSame(container, self); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_CreateChild() + { + var container = new StashboxContainer(); + var newContainer = container.CreateChildContainer(); + var self = newContainer.Resolve(); + + Assert.IsNotNull(self); + Assert.AreSame(newContainer, self); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_Dependency_CreateChild() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var newContainer = container.CreateChildContainer(); + var inst = newContainer.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(newContainer, inst.Container); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_Dependency_BeginScope() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var scope = container.BeginScope(); + var inst = scope.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(container, inst.Container); + } + + [TestMethod] + public void ContainerTests_ResolutionScopeResolve_Dependency() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var inst = container.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(container.RootScope, inst.ResolutionScope); + } + + [TestMethod] + public void ContainerTests_ResolutionScopeResolve_Dependency_BeginScope() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var newScope = container.BeginScope(); + var inst = newScope.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(newScope, inst.ResolutionScope); + } + public interface ITest1 { } public interface ITest2 { } @@ -159,6 +255,18 @@ public Test4(ITest3 test3) } } + public class Test5 + { + public IResolutionScope ResolutionScope { get; } + public IStashboxContainer Container { get; } + + public Test5(IResolutionScope resolutionScope, IStashboxContainer container) + { + ResolutionScope = resolutionScope; + Container = container; + } + } + public class TestResolver : Resolver { public override bool SupportsMany => true; diff --git a/src/stashbox/Resolution/ResolutionStrategy.cs b/src/stashbox/Resolution/ResolutionStrategy.cs index 6e687a02..8961c82f 100644 --- a/src/stashbox/Resolution/ResolutionStrategy.cs +++ b/src/stashbox/Resolution/ResolutionStrategy.cs @@ -18,6 +18,12 @@ internal ResolutionStrategy(IResolverSelector resolverSelector) public Expression BuildResolutionExpression(IContainerContext containerContext, ResolutionInfo resolutionInfo, TypeInformation typeInformation, InjectionParameter[] injectionParameters) { + if (typeInformation.Type == Constants.ResolutionScopeType) + return Expression.Constant(resolutionInfo.ResolutionScope); + + if (typeInformation.Type == Constants.StashboxContainerType) + return Expression.Constant(containerContext.Container); + if (resolutionInfo.ParameterExpressions != null && resolutionInfo.ParameterExpressions.Any(p => p.Type == typeInformation.Type)) return resolutionInfo.ParameterExpressions.Last(p => p.Type == typeInformation.Type); From 932c840859fb911e5bc475dea9e6437a71d82003 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 00:39:44 +0100 Subject: [PATCH 16/27] Reverted scope related changes, refactored disposable handling --- src/stashbox.tests/ContainerTests.cs | 110 +----------------- src/stashbox.tests/LifetimeTests.cs | 9 +- .../Infrastructure/IDependencyResolver.cs | 8 +- src/stashbox/Infrastructure/ILifetime.cs | 5 + .../Infrastructure/IResolutionScope.cs | 8 +- .../Infrastructure/IStashboxContainer.cs | 13 +-- src/stashbox/Lifetime/LifetimeBase.cs | 22 +++- src/stashbox/Lifetime/ScopedLifetime.cs | 16 +-- src/stashbox/Lifetime/SingletonLifetime.cs | 11 +- src/stashbox/Lifetime/TransientLifetime.cs | 16 +++ .../Registration/RegistrationContextBase.cs | 5 +- .../Registration/ServiceRegistration.cs | 21 +--- src/stashbox/Resolution/ActivationContext.cs | 6 - src/stashbox/Resolution/ResolutionStrategy.cs | 6 - src/stashbox/ResolutionScope.cs | 45 +------ src/stashbox/ResolutionScopeBase.cs | 60 ++++++++++ src/stashbox/StashboxContainer.Resolver.cs | 14 +-- src/stashbox/StashboxContainer.cs | 26 ++--- 18 files changed, 150 insertions(+), 251 deletions(-) create mode 100644 src/stashbox/Lifetime/TransientLifetime.cs create mode 100644 src/stashbox/ResolutionScopeBase.cs diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index c27184bf..67e779b4 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -126,103 +126,7 @@ public void ContainerTests_ResolverTest_SupportsMany() Assert.IsInstanceOfType(inst.First(), typeof(Test1)); } - - [TestMethod] - public void ContainerTests_ResolutionScopeResolve() - { - var container = new StashboxContainer(); - var scope = container.Resolve(); - - Assert.IsNotNull(scope); - Assert.AreSame(container.RootScope, scope); - } - - [TestMethod] - public void ContainerTests_ResolutionScopeResolve_BeginScope() - { - var container = new StashboxContainer(); - var newScope = container.BeginScope(); - var scope = newScope.Resolve(); - - Assert.IsNotNull(scope); - Assert.AreSame(newScope, scope); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve() - { - var container = new StashboxContainer(); - var self = container.Resolve(); - - Assert.IsNotNull(self); - Assert.AreSame(container, self); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_BeginScope() - { - var container = new StashboxContainer(); - var newScope = container.BeginScope(); - var self = newScope.Resolve(); - - Assert.IsNotNull(self); - Assert.AreSame(container, self); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_CreateChild() - { - var container = new StashboxContainer(); - var newContainer = container.CreateChildContainer(); - var self = newContainer.Resolve(); - - Assert.IsNotNull(self); - Assert.AreSame(newContainer, self); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_Dependency_CreateChild() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var newContainer = container.CreateChildContainer(); - var inst = newContainer.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(newContainer, inst.Container); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_Dependency_BeginScope() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var scope = container.BeginScope(); - var inst = scope.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(container, inst.Container); - } - - [TestMethod] - public void ContainerTests_ResolutionScopeResolve_Dependency() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var inst = container.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(container.RootScope, inst.ResolutionScope); - } - - [TestMethod] - public void ContainerTests_ResolutionScopeResolve_Dependency_BeginScope() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var newScope = container.BeginScope(); - var inst = newScope.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(newScope, inst.ResolutionScope); - } - + public interface ITest1 { } public interface ITest2 { } @@ -255,18 +159,6 @@ public Test4(ITest3 test3) } } - public class Test5 - { - public IResolutionScope ResolutionScope { get; } - public IStashboxContainer Container { get; } - - public Test5(IResolutionScope resolutionScope, IStashboxContainer container) - { - ResolutionScope = resolutionScope; - Container = container; - } - } - public class TestResolver : Resolver { public override bool SupportsMany => true; diff --git a/src/stashbox.tests/LifetimeTests.cs b/src/stashbox.tests/LifetimeTests.cs index b7ec0106..a54c6350 100644 --- a/src/stashbox.tests/LifetimeTests.cs +++ b/src/stashbox.tests/LifetimeTests.cs @@ -80,12 +80,19 @@ public void LifetimeTests_Resolve_Parallel_Lazy() public void LifetimeTests_StateCheck() { var scoped = new ScopedLifetime(); - Assert.IsTrue(scoped.HandlesObjectDisposal); + Assert.IsFalse(scoped.IsTransient); + Assert.IsFalse(scoped.HandlesObjectDisposal); Assert.IsInstanceOfType(scoped.Create(), typeof(ScopedLifetime)); var singleton = new SingletonLifetime(); + Assert.IsFalse(singleton.IsTransient); Assert.IsTrue(singleton.HandlesObjectDisposal); Assert.IsInstanceOfType(singleton.Create(), typeof(SingletonLifetime)); + + var transient = new TransientLifetime(); + Assert.IsTrue(transient.IsTransient); + Assert.IsFalse(transient.HandlesObjectDisposal); + Assert.IsInstanceOfType(transient.Create(), typeof(TransientLifetime)); } public interface ITest1 { string Name { get; set; } } diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index d038f0ae..9c843e37 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -7,7 +7,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency resolver. /// - public interface IDependencyResolver + public interface IDependencyResolver : IDisposable { /// /// Resolves an instance from the container. @@ -52,5 +52,11 @@ IEnumerable ResolveAll() /// The parameter type. /// The factory delegate. Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes); + + /// + /// Begins a new scope. + /// + /// The new scope. + IDependencyResolver BeginScope(); } } diff --git a/src/stashbox/Infrastructure/ILifetime.cs b/src/stashbox/Infrastructure/ILifetime.cs index 906b03e7..8ea3e5c4 100644 --- a/src/stashbox/Infrastructure/ILifetime.cs +++ b/src/stashbox/Infrastructure/ILifetime.cs @@ -14,6 +14,11 @@ public interface ILifetime /// bool HandlesObjectDisposal { get; } + /// + /// Indicates that the lifetime transient services or not. + /// + bool IsTransient { get; } + /// /// Gets the expression for getting the instance managed by the /// diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs index 22236ce3..798b697b 100644 --- a/src/stashbox/Infrastructure/IResolutionScope.cs +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -5,7 +5,7 @@ namespace Stashbox.Infrastructure /// /// Represents a resolution scope. /// - public interface IResolutionScope : IDependencyResolver, IDisposable + public interface IResolutionScope : IDisposable { /// /// Adds or updates an item in the scope. @@ -29,11 +29,5 @@ public interface IResolutionScope : IDependencyResolver, IDisposable /// The object. TDisposable AddDisposableTracking(TDisposable disposable) where TDisposable : IDisposable; - - /// - /// Begins a new scope. - /// - /// The new scope. - IResolutionScope BeginScope(); } } diff --git a/src/stashbox/Infrastructure/IStashboxContainer.cs b/src/stashbox/Infrastructure/IStashboxContainer.cs index cc398dc9..485e0d28 100644 --- a/src/stashbox/Infrastructure/IStashboxContainer.cs +++ b/src/stashbox/Infrastructure/IStashboxContainer.cs @@ -7,7 +7,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency injection container. /// - public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator, IDisposable + public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator { /// /// Registers a into the container. @@ -36,11 +36,6 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// IContainerContext ContainerContext { get; } - /// - /// The activation context. - /// - IResolutionScope RootScope { get; } - /// /// Checks a type can be resolved by the container. /// @@ -57,12 +52,6 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// True if the service can be resolved, otherwise false. bool CanResolve(Type typeFrom, string name = null); - /// - /// Begins a new scope. - /// - /// The new scope. - IResolutionScope BeginScope(); - /// /// Builds up an instance, the container will perform injections and extensions on it. /// diff --git a/src/stashbox/Lifetime/LifetimeBase.cs b/src/stashbox/Lifetime/LifetimeBase.cs index 373112f0..39c19a82 100644 --- a/src/stashbox/Lifetime/LifetimeBase.cs +++ b/src/stashbox/Lifetime/LifetimeBase.cs @@ -1,7 +1,9 @@ using System; +using System.Linq; using Stashbox.Entity; using Stashbox.Infrastructure; using System.Linq.Expressions; +using System.Reflection; namespace Stashbox.Lifetime { @@ -13,10 +15,26 @@ public abstract class LifetimeBase : ILifetime /// public virtual bool HandlesObjectDisposal => false; + /// + public virtual bool IsTransient => false; + /// public virtual Expression GetExpression(IContainerContext containerContext, IObjectBuilder objectBuilder, - ResolutionInfo resolutionInfo, Type resolveType) => - objectBuilder.GetExpression(resolutionInfo, resolveType); + ResolutionInfo resolutionInfo, Type resolveType) + { + var expr = objectBuilder.GetExpression(resolutionInfo, resolveType); + + if (expr == null) + return null; + + if (!expr.Type.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType) || + !containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled && + !this.IsTransient || objectBuilder.HandlesObjectDisposal || this.HandlesObjectDisposal) + return expr; + + var method = Constants.AddDisposalMethod.MakeGenericMethod(expr.Type); + return Expression.Call(Constants.ScopeExpression, method, expr); + } /// public abstract ILifetime Create(); diff --git a/src/stashbox/Lifetime/ScopedLifetime.cs b/src/stashbox/Lifetime/ScopedLifetime.cs index c5cfaf0e..2593d1c2 100644 --- a/src/stashbox/Lifetime/ScopedLifetime.cs +++ b/src/stashbox/Lifetime/ScopedLifetime.cs @@ -43,26 +43,20 @@ public override Expression GetExpression(IContainerContext containerContext, IOb this.expression = Expression.Call(method, Constants.ScopeExpression, - Expression.Constant(factory), Expression.Constant(this.scopeId)); + Expression.Constant(factory), + Expression.Constant(this.scopeId)); } return this.expression; } - /// - public override bool HandlesObjectDisposal => true; - private static TValue GetScopedValue(IResolutionScope scope, Func factory, string scopeId) { var value = scope.GetScopedItemOrDefault(scopeId); - if(value == null) - { - value = factory(scope); - scope.AddOrUpdateScopedItem(scopeId, value); + if (value != null) return (TValue)value; - if (value is IDisposable disposable) - scope.AddDisposableTracking(disposable); - } + value = factory(scope); + scope.AddOrUpdateScopedItem(scopeId, value); return (TValue)value; } diff --git a/src/stashbox/Lifetime/SingletonLifetime.cs b/src/stashbox/Lifetime/SingletonLifetime.cs index 458718e7..a1d3b5f9 100644 --- a/src/stashbox/Lifetime/SingletonLifetime.cs +++ b/src/stashbox/Lifetime/SingletonLifetime.cs @@ -10,8 +10,8 @@ namespace Stashbox.Lifetime /// public class SingletonLifetime : LifetimeBase { - private volatile Expression expression; private object instance; + private volatile Expression expression; private readonly object syncObject = new object(); /// @@ -25,10 +25,7 @@ public override Expression GetExpression(IContainerContext containerContext, IOb if (expr == null) return null; - if (expr.NodeType == ExpressionType.New && ((NewExpression)expr).Arguments.Count == 0) - this.instance = Activator.CreateInstance(expr.Type); - else - this.instance = expr.CompileDelegate(Constants.ScopeExpression)(resolutionInfo.ResolutionScope); + this.instance = expr.CompileDelegate(Constants.ScopeExpression)(resolutionInfo.ResolutionScope); this.expression = Expression.Constant(this.instance); } @@ -36,10 +33,10 @@ public override Expression GetExpression(IContainerContext containerContext, IOb } /// - public override bool HandlesObjectDisposal => true; + public override ILifetime Create() => new SingletonLifetime(); /// - public override ILifetime Create() => new SingletonLifetime(); + public override bool HandlesObjectDisposal => true; /// public override void CleanUp() diff --git a/src/stashbox/Lifetime/TransientLifetime.cs b/src/stashbox/Lifetime/TransientLifetime.cs new file mode 100644 index 00000000..b4464ac5 --- /dev/null +++ b/src/stashbox/Lifetime/TransientLifetime.cs @@ -0,0 +1,16 @@ +using Stashbox.Infrastructure; + +namespace Stashbox.Lifetime +{ + /// + /// Represents a transient lifetime. + /// + public class TransientLifetime : LifetimeBase + { + /// + public override bool IsTransient => true; + + /// + public override ILifetime Create() => new TransientLifetime(); + } +} diff --git a/src/stashbox/Registration/RegistrationContextBase.cs b/src/stashbox/Registration/RegistrationContextBase.cs index 1b4aa4ad..dae595cf 100644 --- a/src/stashbox/Registration/RegistrationContextBase.cs +++ b/src/stashbox/Registration/RegistrationContextBase.cs @@ -6,6 +6,7 @@ using Stashbox.MetaInfo; using Stashbox.Utils; using System; +using Stashbox.Lifetime; namespace Stashbox.Registration { @@ -90,7 +91,7 @@ private IServiceRegistration ProduceServiceRegistration(ILifetime lifeTime, IObj this.RegistrationContextData.TargetTypeCondition, this.RegistrationContextData.ResolutionCondition); } - private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? null : - this.RegistrationContextData.Lifetime; + private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? null : + this.RegistrationContextData.Lifetime ?? new TransientLifetime(); } } diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index 87ea7dd0..dc14ca38 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; namespace Stashbox.Registration { @@ -71,32 +70,20 @@ public bool IsUsableForCurrentContext(TypeInformation typeInfo) => this.TargetTy /// public bool ValidateGenericContraints(Type type) => !this.metaInfoProvider.HasGenericTypeConstraints || this.metaInfoProvider.ValidateGenericContraints(type); - + /// public void CleanUp() { - this.ObjectBuilder.CleanUp(); this.LifetimeManager?.CleanUp(); + this.ObjectBuilder.CleanUp(); } /// public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - var expr = this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? - this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : + return this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? + this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); - - if (expr == null) - return null; - - if (!this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled || - this.LifetimeManager != null && this.LifetimeManager.HandlesObjectDisposal || - this.ObjectBuilder.HandlesObjectDisposal || - !this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) return expr; - - var method = Constants.AddDisposalMethod.MakeGenericMethod(this.ImplementationType); - - return Expression.Call(Constants.ScopeExpression, method, Expression.Convert(expr, this.ImplementationType)); } } } diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index 8f9ef88c..ab63f744 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -33,12 +33,6 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco private object ActivateType(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) { - if (type == Constants.ResolutionScopeType) - return resolutionScope; - - if (type == Constants.StashboxContainerType) - return this.containerContext.Container; - var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) { diff --git a/src/stashbox/Resolution/ResolutionStrategy.cs b/src/stashbox/Resolution/ResolutionStrategy.cs index 8961c82f..6e687a02 100644 --- a/src/stashbox/Resolution/ResolutionStrategy.cs +++ b/src/stashbox/Resolution/ResolutionStrategy.cs @@ -18,12 +18,6 @@ internal ResolutionStrategy(IResolverSelector resolverSelector) public Expression BuildResolutionExpression(IContainerContext containerContext, ResolutionInfo resolutionInfo, TypeInformation typeInformation, InjectionParameter[] injectionParameters) { - if (typeInformation.Type == Constants.ResolutionScopeType) - return Expression.Constant(resolutionInfo.ResolutionScope); - - if (typeInformation.Type == Constants.StashboxContainerType) - return Expression.Constant(containerContext.Container); - if (resolutionInfo.ParameterExpressions != null && resolutionInfo.ParameterExpressions.Any(p => p.Type == typeInformation.Type)) return resolutionInfo.ParameterExpressions.Last(p => p.Type == typeInformation.Type); diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index cac7fa9c..3f856f2d 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -2,19 +2,15 @@ using System.Collections.Generic; using Stashbox.Infrastructure; using Stashbox.Infrastructure.Resolution; -using Stashbox.Utils; namespace Stashbox { /// /// Represents a resolution scope. /// - public class ResolutionScope : IResolutionScope + public class ResolutionScope : ResolutionScopeBase, IDependencyResolver { private readonly IActivationContext activationContext; - private readonly AtomicBool disposed; - private readonly ConcurrentStore disposableObjects; - private readonly ConcurrentTree scopedItems; /// /// Constructs a . @@ -22,27 +18,8 @@ public class ResolutionScope : IResolutionScope public ResolutionScope(IActivationContext activationContext) { this.activationContext = activationContext; - this.disposed = new AtomicBool(); - this.disposableObjects = new ConcurrentStore(); - this.scopedItems = new ConcurrentTree(); } - /// - public TDisposable AddDisposableTracking(TDisposable disposable) - where TDisposable : IDisposable - { - this.disposableObjects.Add(disposable); - return disposable; - } - - /// - public void AddOrUpdateScopedItem(object key, object value) => - this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); - - /// - public object GetScopedItemOrDefault(object key) => - this.scopedItems.GetOrDefault(key); - /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; @@ -67,24 +44,6 @@ public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResul this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); /// - public IResolutionScope BeginScope() => new ResolutionScope(this.activationContext); - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes the scope. - /// - /// Indicates the scope is disposing or not. - protected virtual void Dispose(bool disposing) - { - if (!this.disposed.CompareExchange(false, true) || !disposing) return; - foreach (var disposableObject in disposableObjects) - disposableObject?.Dispose(); - } + public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext); } } diff --git a/src/stashbox/ResolutionScopeBase.cs b/src/stashbox/ResolutionScopeBase.cs new file mode 100644 index 00000000..87e93982 --- /dev/null +++ b/src/stashbox/ResolutionScopeBase.cs @@ -0,0 +1,60 @@ +using System; +using Stashbox.Infrastructure; +using Stashbox.Utils; + +namespace Stashbox +{ + /// + /// Represents the base class for a resolution scope. + /// + public class ResolutionScopeBase : IResolutionScope + { + private readonly AtomicBool disposed; + private readonly ConcurrentStore disposableObjects; + private readonly ConcurrentTree scopedItems; + + /// + /// Constructs a . + /// + public ResolutionScopeBase() + { + this.disposed = new AtomicBool(); + this.disposableObjects = new ConcurrentStore(); + this.scopedItems = new ConcurrentTree(); + } + + /// + public TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable + { + this.disposableObjects.Add(disposable); + return disposable; + } + + /// + public void AddOrUpdateScopedItem(object key, object value) => + this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); + + /// + public object GetScopedItemOrDefault(object key) => + this.scopedItems.GetOrDefault(key); + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the scope. + /// + /// Indicates the scope is disposing or not. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed.CompareExchange(false, true) || !disposing) return; + foreach (var disposableObject in disposableObjects) + disposableObject?.Dispose(); + } + } +} diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index ea2114f3..893bd111 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -1,5 +1,4 @@ -using Stashbox.Utils; -using System; +using System; using System.Collections.Generic; namespace Stashbox @@ -8,26 +7,25 @@ public partial class StashboxContainer { /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.activationContext.Activate(typeof(TKey), this.RootScope, name, nullResultAllowed) as TKey; + this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.activationContext.Activate(typeFrom, this.RootScope, name, nullResultAllowed); + this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.activationContext.Activate(typeof(IEnumerable), this.RootScope) as IEnumerable; + this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { - Shield.EnsureNotNull(typeFrom, nameof(typeFrom)); var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.activationContext.Activate(type, this.RootScope); + return (IEnumerable)this.activationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.activationContext.ActivateFactory(typeFrom, parameterTypes, this.RootScope, name, nullResultAllowed); + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); } } diff --git a/src/stashbox/StashboxContainer.cs b/src/stashbox/StashboxContainer.cs index 1d5b5ea8..d197544e 100644 --- a/src/stashbox/StashboxContainer.cs +++ b/src/stashbox/StashboxContainer.cs @@ -19,7 +19,7 @@ namespace Stashbox /// /// Represents the stashbox dependency injection container. /// - public partial class StashboxContainer : IStashboxContainer + public partial class StashboxContainer : ResolutionScopeBase, IStashboxContainer { private readonly IContainerExtensionManager containerExtensionManager; private readonly IResolverSelector resolverSelector; @@ -45,7 +45,6 @@ public StashboxContainer(Action config = null) this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), configurator, new DecoratorRepository()); this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); - this.RootScope = new ResolutionScope(this.activationContext); this.RegisterResolvers(); } @@ -61,7 +60,6 @@ internal StashboxContainer(IStashboxContainer parentContainer, IContainerExtensi this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), parentContainer.ContainerContext.ContainerConfigurator, parentContainer.ContainerContext.DecoratorRepository); this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); - this.RootScope = new ResolutionScope(this.activationContext); this.containerExtensionManager.ReinitalizeExtensions(this.ContainerContext); } @@ -90,7 +88,7 @@ public void Validate() { foreach (var serviceRegistration in this.registrationRepository.GetAllRegistrations()) { - var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this.RootScope), serviceRegistration.ServiceType); + var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this), serviceRegistration.ServiceType); if (expression == null) throw new ResolutionFailedException(serviceRegistration.ImplementationType.FullName); } @@ -102,15 +100,12 @@ public void Validate() /// public IContainerContext ContainerContext { get; } - /// - public IResolutionScope RootScope { get; } - /// public IStashboxContainer CreateChildContainer() => new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector, this.expressionBuilder); /// - public IResolutionScope BeginScope() => this.RootScope.BeginScope(); + public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext); /// public void Configure(Action config) => @@ -133,30 +128,23 @@ public TTo BuildUp(TTo instance) where TTo : class var typeTo = instance.GetType(); var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, typeTo); - var resolutionInfo = ResolutionInfo.New(this.RootScope); + var resolutionInfo = ResolutionInfo.New(this); var expr = this.expressionBuilder.CreateFillExpression(this.containerExtensionManager, this.ContainerContext, Expression.Constant(instance), resolutionInfo, typeTo, null, metaInfoProvider.GetResolutionMembers(resolutionInfo), metaInfoProvider.GetResolutionMethods(resolutionInfo)); var factory = expr.CompileDelegate(Constants.ScopeExpression); - return factory(this.RootScope) as TTo; - } - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); + return factory(this) as TTo; } /// /// Disposes the container. /// /// Indicates the container is disposing or not. - protected virtual void Dispose(bool disposing) + protected override void Dispose(bool disposing) { if (!this.disposed.CompareExchange(false, true) || !disposing) return; - this.RootScope.Dispose(); + base.Dispose(true); this.registrationRepository.CleanUp(); this.containerExtensionManager.CleanUp(); } From 38392a05d0d526ff95cf86babdc14c8221f23450 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 01:10:57 +0100 Subject: [PATCH 17/27] Small fixes --- src/stashbox.tests/LifetimeTests.cs | 7 ------- src/stashbox/Infrastructure/ILifetime.cs | 5 ----- src/stashbox/Lifetime/LifetimeBase.cs | 6 +----- src/stashbox/Lifetime/TransientLifetime.cs | 16 ---------------- .../Registration/RegistrationContextBase.cs | 2 +- .../Registration/ServiceRegistration.cs | 17 +++++++++++++++-- 6 files changed, 17 insertions(+), 36 deletions(-) delete mode 100644 src/stashbox/Lifetime/TransientLifetime.cs diff --git a/src/stashbox.tests/LifetimeTests.cs b/src/stashbox.tests/LifetimeTests.cs index a54c6350..24dc0554 100644 --- a/src/stashbox.tests/LifetimeTests.cs +++ b/src/stashbox.tests/LifetimeTests.cs @@ -80,19 +80,12 @@ public void LifetimeTests_Resolve_Parallel_Lazy() public void LifetimeTests_StateCheck() { var scoped = new ScopedLifetime(); - Assert.IsFalse(scoped.IsTransient); Assert.IsFalse(scoped.HandlesObjectDisposal); Assert.IsInstanceOfType(scoped.Create(), typeof(ScopedLifetime)); var singleton = new SingletonLifetime(); - Assert.IsFalse(singleton.IsTransient); Assert.IsTrue(singleton.HandlesObjectDisposal); Assert.IsInstanceOfType(singleton.Create(), typeof(SingletonLifetime)); - - var transient = new TransientLifetime(); - Assert.IsTrue(transient.IsTransient); - Assert.IsFalse(transient.HandlesObjectDisposal); - Assert.IsInstanceOfType(transient.Create(), typeof(TransientLifetime)); } public interface ITest1 { string Name { get; set; } } diff --git a/src/stashbox/Infrastructure/ILifetime.cs b/src/stashbox/Infrastructure/ILifetime.cs index 8ea3e5c4..906b03e7 100644 --- a/src/stashbox/Infrastructure/ILifetime.cs +++ b/src/stashbox/Infrastructure/ILifetime.cs @@ -14,11 +14,6 @@ public interface ILifetime /// bool HandlesObjectDisposal { get; } - /// - /// Indicates that the lifetime transient services or not. - /// - bool IsTransient { get; } - /// /// Gets the expression for getting the instance managed by the /// diff --git a/src/stashbox/Lifetime/LifetimeBase.cs b/src/stashbox/Lifetime/LifetimeBase.cs index 39c19a82..ff7a5e09 100644 --- a/src/stashbox/Lifetime/LifetimeBase.cs +++ b/src/stashbox/Lifetime/LifetimeBase.cs @@ -15,9 +15,6 @@ public abstract class LifetimeBase : ILifetime /// public virtual bool HandlesObjectDisposal => false; - /// - public virtual bool IsTransient => false; - /// public virtual Expression GetExpression(IContainerContext containerContext, IObjectBuilder objectBuilder, ResolutionInfo resolutionInfo, Type resolveType) @@ -28,8 +25,7 @@ public virtual Expression GetExpression(IContainerContext containerContext, IObj return null; if (!expr.Type.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType) || - !containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled && - !this.IsTransient || objectBuilder.HandlesObjectDisposal || this.HandlesObjectDisposal) + objectBuilder.HandlesObjectDisposal || this.HandlesObjectDisposal) return expr; var method = Constants.AddDisposalMethod.MakeGenericMethod(expr.Type); diff --git a/src/stashbox/Lifetime/TransientLifetime.cs b/src/stashbox/Lifetime/TransientLifetime.cs deleted file mode 100644 index b4464ac5..00000000 --- a/src/stashbox/Lifetime/TransientLifetime.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Stashbox.Infrastructure; - -namespace Stashbox.Lifetime -{ - /// - /// Represents a transient lifetime. - /// - public class TransientLifetime : LifetimeBase - { - /// - public override bool IsTransient => true; - - /// - public override ILifetime Create() => new TransientLifetime(); - } -} diff --git a/src/stashbox/Registration/RegistrationContextBase.cs b/src/stashbox/Registration/RegistrationContextBase.cs index dae595cf..8c70fe0c 100644 --- a/src/stashbox/Registration/RegistrationContextBase.cs +++ b/src/stashbox/Registration/RegistrationContextBase.cs @@ -92,6 +92,6 @@ private IServiceRegistration ProduceServiceRegistration(ILifetime lifeTime, IObj } private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? null : - this.RegistrationContextData.Lifetime ?? new TransientLifetime(); + this.RegistrationContextData.Lifetime; } } diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index dc14ca38..e02ebf0c 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; namespace Stashbox.Registration { @@ -81,9 +82,21 @@ public void CleanUp() /// public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - return this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? - this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : + var expression = this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? + this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); + + if (expression == null) + return null; + + if (this.LifetimeManager == null && this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled && + expression.Type.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType) && !this.ObjectBuilder.HandlesObjectDisposal) + { + var method = Constants.AddDisposalMethod.MakeGenericMethod(expression.Type); + return Expression.Call(Constants.ScopeExpression, method, expression); + } + + return expression; } } } From 19cca9b556633ac9d0371f352537587d48b12ec8 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 02:28:11 +0100 Subject: [PATCH 18/27] Revert "Small fixes" This reverts commit 38392a05d0d526ff95cf86babdc14c8221f23450. --- src/stashbox.tests/LifetimeTests.cs | 7 +++++++ src/stashbox/Infrastructure/ILifetime.cs | 5 +++++ src/stashbox/Lifetime/LifetimeBase.cs | 6 +++++- src/stashbox/Lifetime/TransientLifetime.cs | 16 ++++++++++++++++ .../Registration/RegistrationContextBase.cs | 2 +- .../Registration/ServiceRegistration.cs | 17 ++--------------- 6 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 src/stashbox/Lifetime/TransientLifetime.cs diff --git a/src/stashbox.tests/LifetimeTests.cs b/src/stashbox.tests/LifetimeTests.cs index 24dc0554..a54c6350 100644 --- a/src/stashbox.tests/LifetimeTests.cs +++ b/src/stashbox.tests/LifetimeTests.cs @@ -80,12 +80,19 @@ public void LifetimeTests_Resolve_Parallel_Lazy() public void LifetimeTests_StateCheck() { var scoped = new ScopedLifetime(); + Assert.IsFalse(scoped.IsTransient); Assert.IsFalse(scoped.HandlesObjectDisposal); Assert.IsInstanceOfType(scoped.Create(), typeof(ScopedLifetime)); var singleton = new SingletonLifetime(); + Assert.IsFalse(singleton.IsTransient); Assert.IsTrue(singleton.HandlesObjectDisposal); Assert.IsInstanceOfType(singleton.Create(), typeof(SingletonLifetime)); + + var transient = new TransientLifetime(); + Assert.IsTrue(transient.IsTransient); + Assert.IsFalse(transient.HandlesObjectDisposal); + Assert.IsInstanceOfType(transient.Create(), typeof(TransientLifetime)); } public interface ITest1 { string Name { get; set; } } diff --git a/src/stashbox/Infrastructure/ILifetime.cs b/src/stashbox/Infrastructure/ILifetime.cs index 906b03e7..8ea3e5c4 100644 --- a/src/stashbox/Infrastructure/ILifetime.cs +++ b/src/stashbox/Infrastructure/ILifetime.cs @@ -14,6 +14,11 @@ public interface ILifetime /// bool HandlesObjectDisposal { get; } + /// + /// Indicates that the lifetime transient services or not. + /// + bool IsTransient { get; } + /// /// Gets the expression for getting the instance managed by the /// diff --git a/src/stashbox/Lifetime/LifetimeBase.cs b/src/stashbox/Lifetime/LifetimeBase.cs index ff7a5e09..39c19a82 100644 --- a/src/stashbox/Lifetime/LifetimeBase.cs +++ b/src/stashbox/Lifetime/LifetimeBase.cs @@ -15,6 +15,9 @@ public abstract class LifetimeBase : ILifetime /// public virtual bool HandlesObjectDisposal => false; + /// + public virtual bool IsTransient => false; + /// public virtual Expression GetExpression(IContainerContext containerContext, IObjectBuilder objectBuilder, ResolutionInfo resolutionInfo, Type resolveType) @@ -25,7 +28,8 @@ public virtual Expression GetExpression(IContainerContext containerContext, IObj return null; if (!expr.Type.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType) || - objectBuilder.HandlesObjectDisposal || this.HandlesObjectDisposal) + !containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled && + !this.IsTransient || objectBuilder.HandlesObjectDisposal || this.HandlesObjectDisposal) return expr; var method = Constants.AddDisposalMethod.MakeGenericMethod(expr.Type); diff --git a/src/stashbox/Lifetime/TransientLifetime.cs b/src/stashbox/Lifetime/TransientLifetime.cs new file mode 100644 index 00000000..b4464ac5 --- /dev/null +++ b/src/stashbox/Lifetime/TransientLifetime.cs @@ -0,0 +1,16 @@ +using Stashbox.Infrastructure; + +namespace Stashbox.Lifetime +{ + /// + /// Represents a transient lifetime. + /// + public class TransientLifetime : LifetimeBase + { + /// + public override bool IsTransient => true; + + /// + public override ILifetime Create() => new TransientLifetime(); + } +} diff --git a/src/stashbox/Registration/RegistrationContextBase.cs b/src/stashbox/Registration/RegistrationContextBase.cs index 8c70fe0c..dae595cf 100644 --- a/src/stashbox/Registration/RegistrationContextBase.cs +++ b/src/stashbox/Registration/RegistrationContextBase.cs @@ -92,6 +92,6 @@ private IServiceRegistration ProduceServiceRegistration(ILifetime lifeTime, IObj } private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? null : - this.RegistrationContextData.Lifetime; + this.RegistrationContextData.Lifetime ?? new TransientLifetime(); } } diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index e02ebf0c..dc14ca38 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; namespace Stashbox.Registration { @@ -82,21 +81,9 @@ public void CleanUp() /// public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - var expression = this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? - this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : + return this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? + this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); - - if (expression == null) - return null; - - if (this.LifetimeManager == null && this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled && - expression.Type.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType) && !this.ObjectBuilder.HandlesObjectDisposal) - { - var method = Constants.AddDisposalMethod.MakeGenericMethod(expression.Type); - return Expression.Call(Constants.ScopeExpression, method, expression); - } - - return expression; } } } From 9d02a9f2cc7199265ffc5b4a28e0d46a11a4ee74 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 02:29:27 +0100 Subject: [PATCH 19/27] Revert "Reverted scope related changes, refactored disposable handling" This reverts commit 932c840859fb911e5bc475dea9e6437a71d82003. --- src/stashbox.tests/ContainerTests.cs | 110 +++++++++++++++++- src/stashbox.tests/LifetimeTests.cs | 9 +- .../Infrastructure/IDependencyResolver.cs | 8 +- src/stashbox/Infrastructure/ILifetime.cs | 5 - .../Infrastructure/IResolutionScope.cs | 8 +- .../Infrastructure/IStashboxContainer.cs | 13 ++- src/stashbox/Lifetime/LifetimeBase.cs | 22 +--- src/stashbox/Lifetime/ScopedLifetime.cs | 16 ++- src/stashbox/Lifetime/SingletonLifetime.cs | 11 +- src/stashbox/Lifetime/TransientLifetime.cs | 16 --- .../Registration/RegistrationContextBase.cs | 5 +- .../Registration/ServiceRegistration.cs | 21 +++- src/stashbox/Resolution/ActivationContext.cs | 6 + src/stashbox/Resolution/ResolutionStrategy.cs | 6 + src/stashbox/ResolutionScope.cs | 45 ++++++- src/stashbox/ResolutionScopeBase.cs | 60 ---------- src/stashbox/StashboxContainer.Resolver.cs | 14 ++- src/stashbox/StashboxContainer.cs | 26 +++-- 18 files changed, 251 insertions(+), 150 deletions(-) delete mode 100644 src/stashbox/Lifetime/TransientLifetime.cs delete mode 100644 src/stashbox/ResolutionScopeBase.cs diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index 67e779b4..c27184bf 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -126,7 +126,103 @@ public void ContainerTests_ResolverTest_SupportsMany() Assert.IsInstanceOfType(inst.First(), typeof(Test1)); } - + + [TestMethod] + public void ContainerTests_ResolutionScopeResolve() + { + var container = new StashboxContainer(); + var scope = container.Resolve(); + + Assert.IsNotNull(scope); + Assert.AreSame(container.RootScope, scope); + } + + [TestMethod] + public void ContainerTests_ResolutionScopeResolve_BeginScope() + { + var container = new StashboxContainer(); + var newScope = container.BeginScope(); + var scope = newScope.Resolve(); + + Assert.IsNotNull(scope); + Assert.AreSame(newScope, scope); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve() + { + var container = new StashboxContainer(); + var self = container.Resolve(); + + Assert.IsNotNull(self); + Assert.AreSame(container, self); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_BeginScope() + { + var container = new StashboxContainer(); + var newScope = container.BeginScope(); + var self = newScope.Resolve(); + + Assert.IsNotNull(self); + Assert.AreSame(container, self); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_CreateChild() + { + var container = new StashboxContainer(); + var newContainer = container.CreateChildContainer(); + var self = newContainer.Resolve(); + + Assert.IsNotNull(self); + Assert.AreSame(newContainer, self); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_Dependency_CreateChild() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var newContainer = container.CreateChildContainer(); + var inst = newContainer.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(newContainer, inst.Container); + } + + [TestMethod] + public void ContainerTests_ContainerSelfResolve_Dependency_BeginScope() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var scope = container.BeginScope(); + var inst = scope.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(container, inst.Container); + } + + [TestMethod] + public void ContainerTests_ResolutionScopeResolve_Dependency() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var inst = container.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(container.RootScope, inst.ResolutionScope); + } + + [TestMethod] + public void ContainerTests_ResolutionScopeResolve_Dependency_BeginScope() + { + var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); + var newScope = container.BeginScope(); + var inst = newScope.Resolve(); + + Assert.IsNotNull(inst); + Assert.AreSame(newScope, inst.ResolutionScope); + } + public interface ITest1 { } public interface ITest2 { } @@ -159,6 +255,18 @@ public Test4(ITest3 test3) } } + public class Test5 + { + public IResolutionScope ResolutionScope { get; } + public IStashboxContainer Container { get; } + + public Test5(IResolutionScope resolutionScope, IStashboxContainer container) + { + ResolutionScope = resolutionScope; + Container = container; + } + } + public class TestResolver : Resolver { public override bool SupportsMany => true; diff --git a/src/stashbox.tests/LifetimeTests.cs b/src/stashbox.tests/LifetimeTests.cs index a54c6350..b7ec0106 100644 --- a/src/stashbox.tests/LifetimeTests.cs +++ b/src/stashbox.tests/LifetimeTests.cs @@ -80,19 +80,12 @@ public void LifetimeTests_Resolve_Parallel_Lazy() public void LifetimeTests_StateCheck() { var scoped = new ScopedLifetime(); - Assert.IsFalse(scoped.IsTransient); - Assert.IsFalse(scoped.HandlesObjectDisposal); + Assert.IsTrue(scoped.HandlesObjectDisposal); Assert.IsInstanceOfType(scoped.Create(), typeof(ScopedLifetime)); var singleton = new SingletonLifetime(); - Assert.IsFalse(singleton.IsTransient); Assert.IsTrue(singleton.HandlesObjectDisposal); Assert.IsInstanceOfType(singleton.Create(), typeof(SingletonLifetime)); - - var transient = new TransientLifetime(); - Assert.IsTrue(transient.IsTransient); - Assert.IsFalse(transient.HandlesObjectDisposal); - Assert.IsInstanceOfType(transient.Create(), typeof(TransientLifetime)); } public interface ITest1 { string Name { get; set; } } diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index 9c843e37..d038f0ae 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -7,7 +7,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency resolver. /// - public interface IDependencyResolver : IDisposable + public interface IDependencyResolver { /// /// Resolves an instance from the container. @@ -52,11 +52,5 @@ IEnumerable ResolveAll() /// The parameter type. /// The factory delegate. Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes); - - /// - /// Begins a new scope. - /// - /// The new scope. - IDependencyResolver BeginScope(); } } diff --git a/src/stashbox/Infrastructure/ILifetime.cs b/src/stashbox/Infrastructure/ILifetime.cs index 8ea3e5c4..906b03e7 100644 --- a/src/stashbox/Infrastructure/ILifetime.cs +++ b/src/stashbox/Infrastructure/ILifetime.cs @@ -14,11 +14,6 @@ public interface ILifetime /// bool HandlesObjectDisposal { get; } - /// - /// Indicates that the lifetime transient services or not. - /// - bool IsTransient { get; } - /// /// Gets the expression for getting the instance managed by the /// diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs index 798b697b..22236ce3 100644 --- a/src/stashbox/Infrastructure/IResolutionScope.cs +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -5,7 +5,7 @@ namespace Stashbox.Infrastructure /// /// Represents a resolution scope. /// - public interface IResolutionScope : IDisposable + public interface IResolutionScope : IDependencyResolver, IDisposable { /// /// Adds or updates an item in the scope. @@ -29,5 +29,11 @@ public interface IResolutionScope : IDisposable /// The object. TDisposable AddDisposableTracking(TDisposable disposable) where TDisposable : IDisposable; + + /// + /// Begins a new scope. + /// + /// The new scope. + IResolutionScope BeginScope(); } } diff --git a/src/stashbox/Infrastructure/IStashboxContainer.cs b/src/stashbox/Infrastructure/IStashboxContainer.cs index 485e0d28..cc398dc9 100644 --- a/src/stashbox/Infrastructure/IStashboxContainer.cs +++ b/src/stashbox/Infrastructure/IStashboxContainer.cs @@ -7,7 +7,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency injection container. /// - public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator + public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator, IDisposable { /// /// Registers a into the container. @@ -36,6 +36,11 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// IContainerContext ContainerContext { get; } + /// + /// The activation context. + /// + IResolutionScope RootScope { get; } + /// /// Checks a type can be resolved by the container. /// @@ -52,6 +57,12 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// True if the service can be resolved, otherwise false. bool CanResolve(Type typeFrom, string name = null); + /// + /// Begins a new scope. + /// + /// The new scope. + IResolutionScope BeginScope(); + /// /// Builds up an instance, the container will perform injections and extensions on it. /// diff --git a/src/stashbox/Lifetime/LifetimeBase.cs b/src/stashbox/Lifetime/LifetimeBase.cs index 39c19a82..373112f0 100644 --- a/src/stashbox/Lifetime/LifetimeBase.cs +++ b/src/stashbox/Lifetime/LifetimeBase.cs @@ -1,9 +1,7 @@ using System; -using System.Linq; using Stashbox.Entity; using Stashbox.Infrastructure; using System.Linq.Expressions; -using System.Reflection; namespace Stashbox.Lifetime { @@ -15,26 +13,10 @@ public abstract class LifetimeBase : ILifetime /// public virtual bool HandlesObjectDisposal => false; - /// - public virtual bool IsTransient => false; - /// public virtual Expression GetExpression(IContainerContext containerContext, IObjectBuilder objectBuilder, - ResolutionInfo resolutionInfo, Type resolveType) - { - var expr = objectBuilder.GetExpression(resolutionInfo, resolveType); - - if (expr == null) - return null; - - if (!expr.Type.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType) || - !containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled && - !this.IsTransient || objectBuilder.HandlesObjectDisposal || this.HandlesObjectDisposal) - return expr; - - var method = Constants.AddDisposalMethod.MakeGenericMethod(expr.Type); - return Expression.Call(Constants.ScopeExpression, method, expr); - } + ResolutionInfo resolutionInfo, Type resolveType) => + objectBuilder.GetExpression(resolutionInfo, resolveType); /// public abstract ILifetime Create(); diff --git a/src/stashbox/Lifetime/ScopedLifetime.cs b/src/stashbox/Lifetime/ScopedLifetime.cs index 2593d1c2..c5cfaf0e 100644 --- a/src/stashbox/Lifetime/ScopedLifetime.cs +++ b/src/stashbox/Lifetime/ScopedLifetime.cs @@ -43,20 +43,26 @@ public override Expression GetExpression(IContainerContext containerContext, IOb this.expression = Expression.Call(method, Constants.ScopeExpression, - Expression.Constant(factory), - Expression.Constant(this.scopeId)); + Expression.Constant(factory), Expression.Constant(this.scopeId)); } return this.expression; } + /// + public override bool HandlesObjectDisposal => true; + private static TValue GetScopedValue(IResolutionScope scope, Func factory, string scopeId) { var value = scope.GetScopedItemOrDefault(scopeId); - if (value != null) return (TValue)value; + if(value == null) + { + value = factory(scope); + scope.AddOrUpdateScopedItem(scopeId, value); - value = factory(scope); - scope.AddOrUpdateScopedItem(scopeId, value); + if (value is IDisposable disposable) + scope.AddDisposableTracking(disposable); + } return (TValue)value; } diff --git a/src/stashbox/Lifetime/SingletonLifetime.cs b/src/stashbox/Lifetime/SingletonLifetime.cs index a1d3b5f9..458718e7 100644 --- a/src/stashbox/Lifetime/SingletonLifetime.cs +++ b/src/stashbox/Lifetime/SingletonLifetime.cs @@ -10,8 +10,8 @@ namespace Stashbox.Lifetime /// public class SingletonLifetime : LifetimeBase { - private object instance; private volatile Expression expression; + private object instance; private readonly object syncObject = new object(); /// @@ -25,7 +25,10 @@ public override Expression GetExpression(IContainerContext containerContext, IOb if (expr == null) return null; - this.instance = expr.CompileDelegate(Constants.ScopeExpression)(resolutionInfo.ResolutionScope); + if (expr.NodeType == ExpressionType.New && ((NewExpression)expr).Arguments.Count == 0) + this.instance = Activator.CreateInstance(expr.Type); + else + this.instance = expr.CompileDelegate(Constants.ScopeExpression)(resolutionInfo.ResolutionScope); this.expression = Expression.Constant(this.instance); } @@ -33,10 +36,10 @@ public override Expression GetExpression(IContainerContext containerContext, IOb } /// - public override ILifetime Create() => new SingletonLifetime(); + public override bool HandlesObjectDisposal => true; /// - public override bool HandlesObjectDisposal => true; + public override ILifetime Create() => new SingletonLifetime(); /// public override void CleanUp() diff --git a/src/stashbox/Lifetime/TransientLifetime.cs b/src/stashbox/Lifetime/TransientLifetime.cs deleted file mode 100644 index b4464ac5..00000000 --- a/src/stashbox/Lifetime/TransientLifetime.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Stashbox.Infrastructure; - -namespace Stashbox.Lifetime -{ - /// - /// Represents a transient lifetime. - /// - public class TransientLifetime : LifetimeBase - { - /// - public override bool IsTransient => true; - - /// - public override ILifetime Create() => new TransientLifetime(); - } -} diff --git a/src/stashbox/Registration/RegistrationContextBase.cs b/src/stashbox/Registration/RegistrationContextBase.cs index dae595cf..1b4aa4ad 100644 --- a/src/stashbox/Registration/RegistrationContextBase.cs +++ b/src/stashbox/Registration/RegistrationContextBase.cs @@ -6,7 +6,6 @@ using Stashbox.MetaInfo; using Stashbox.Utils; using System; -using Stashbox.Lifetime; namespace Stashbox.Registration { @@ -91,7 +90,7 @@ private IServiceRegistration ProduceServiceRegistration(ILifetime lifeTime, IObj this.RegistrationContextData.TargetTypeCondition, this.RegistrationContextData.ResolutionCondition); } - private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? null : - this.RegistrationContextData.Lifetime ?? new TransientLifetime(); + private ILifetime ChooseLifeTime() => this.RegistrationContextData.ExistingInstance != null ? null : + this.RegistrationContextData.Lifetime; } } diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index dc14ca38..87ea7dd0 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; namespace Stashbox.Registration { @@ -70,20 +71,32 @@ public bool IsUsableForCurrentContext(TypeInformation typeInfo) => this.TargetTy /// public bool ValidateGenericContraints(Type type) => !this.metaInfoProvider.HasGenericTypeConstraints || this.metaInfoProvider.ValidateGenericContraints(type); - + /// public void CleanUp() { - this.LifetimeManager?.CleanUp(); this.ObjectBuilder.CleanUp(); + this.LifetimeManager?.CleanUp(); } /// public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - return this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? - this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : + var expr = this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? + this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); + + if (expr == null) + return null; + + if (!this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled || + this.LifetimeManager != null && this.LifetimeManager.HandlesObjectDisposal || + this.ObjectBuilder.HandlesObjectDisposal || + !this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) return expr; + + var method = Constants.AddDisposalMethod.MakeGenericMethod(this.ImplementationType); + + return Expression.Call(Constants.ScopeExpression, method, Expression.Convert(expr, this.ImplementationType)); } } } diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index ab63f744..8f9ef88c 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -33,6 +33,12 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco private object ActivateType(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) { + if (type == Constants.ResolutionScopeType) + return resolutionScope; + + if (type == Constants.StashboxContainerType) + return this.containerContext.Container; + var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) { diff --git a/src/stashbox/Resolution/ResolutionStrategy.cs b/src/stashbox/Resolution/ResolutionStrategy.cs index 6e687a02..8961c82f 100644 --- a/src/stashbox/Resolution/ResolutionStrategy.cs +++ b/src/stashbox/Resolution/ResolutionStrategy.cs @@ -18,6 +18,12 @@ internal ResolutionStrategy(IResolverSelector resolverSelector) public Expression BuildResolutionExpression(IContainerContext containerContext, ResolutionInfo resolutionInfo, TypeInformation typeInformation, InjectionParameter[] injectionParameters) { + if (typeInformation.Type == Constants.ResolutionScopeType) + return Expression.Constant(resolutionInfo.ResolutionScope); + + if (typeInformation.Type == Constants.StashboxContainerType) + return Expression.Constant(containerContext.Container); + if (resolutionInfo.ParameterExpressions != null && resolutionInfo.ParameterExpressions.Any(p => p.Type == typeInformation.Type)) return resolutionInfo.ParameterExpressions.Last(p => p.Type == typeInformation.Type); diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index 3f856f2d..cac7fa9c 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -2,15 +2,19 @@ using System.Collections.Generic; using Stashbox.Infrastructure; using Stashbox.Infrastructure.Resolution; +using Stashbox.Utils; namespace Stashbox { /// /// Represents a resolution scope. /// - public class ResolutionScope : ResolutionScopeBase, IDependencyResolver + public class ResolutionScope : IResolutionScope { private readonly IActivationContext activationContext; + private readonly AtomicBool disposed; + private readonly ConcurrentStore disposableObjects; + private readonly ConcurrentTree scopedItems; /// /// Constructs a . @@ -18,8 +22,27 @@ public class ResolutionScope : ResolutionScopeBase, IDependencyResolver public ResolutionScope(IActivationContext activationContext) { this.activationContext = activationContext; + this.disposed = new AtomicBool(); + this.disposableObjects = new ConcurrentStore(); + this.scopedItems = new ConcurrentTree(); } + /// + public TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable + { + this.disposableObjects.Add(disposable); + return disposable; + } + + /// + public void AddOrUpdateScopedItem(object key, object value) => + this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); + + /// + public object GetScopedItemOrDefault(object key) => + this.scopedItems.GetOrDefault(key); + /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; @@ -44,6 +67,24 @@ public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResul this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); /// - public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext); + public IResolutionScope BeginScope() => new ResolutionScope(this.activationContext); + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the scope. + /// + /// Indicates the scope is disposing or not. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed.CompareExchange(false, true) || !disposing) return; + foreach (var disposableObject in disposableObjects) + disposableObject?.Dispose(); + } } } diff --git a/src/stashbox/ResolutionScopeBase.cs b/src/stashbox/ResolutionScopeBase.cs deleted file mode 100644 index 87e93982..00000000 --- a/src/stashbox/ResolutionScopeBase.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Stashbox.Infrastructure; -using Stashbox.Utils; - -namespace Stashbox -{ - /// - /// Represents the base class for a resolution scope. - /// - public class ResolutionScopeBase : IResolutionScope - { - private readonly AtomicBool disposed; - private readonly ConcurrentStore disposableObjects; - private readonly ConcurrentTree scopedItems; - - /// - /// Constructs a . - /// - public ResolutionScopeBase() - { - this.disposed = new AtomicBool(); - this.disposableObjects = new ConcurrentStore(); - this.scopedItems = new ConcurrentTree(); - } - - /// - public TDisposable AddDisposableTracking(TDisposable disposable) - where TDisposable : IDisposable - { - this.disposableObjects.Add(disposable); - return disposable; - } - - /// - public void AddOrUpdateScopedItem(object key, object value) => - this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); - - /// - public object GetScopedItemOrDefault(object key) => - this.scopedItems.GetOrDefault(key); - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes the scope. - /// - /// Indicates the scope is disposing or not. - protected virtual void Dispose(bool disposing) - { - if (!this.disposed.CompareExchange(false, true) || !disposing) return; - foreach (var disposableObject in disposableObjects) - disposableObject?.Dispose(); - } - } -} diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index 893bd111..ea2114f3 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -1,4 +1,5 @@ -using System; +using Stashbox.Utils; +using System; using System.Collections.Generic; namespace Stashbox @@ -7,25 +8,26 @@ public partial class StashboxContainer { /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; + this.activationContext.Activate(typeof(TKey), this.RootScope, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); + this.activationContext.Activate(typeFrom, this.RootScope, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + this.activationContext.Activate(typeof(IEnumerable), this.RootScope) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { + Shield.EnsureNotNull(typeFrom, nameof(typeFrom)); var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.activationContext.Activate(type, this); + return (IEnumerable)this.activationContext.Activate(type, this.RootScope); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this.RootScope, name, nullResultAllowed); } } diff --git a/src/stashbox/StashboxContainer.cs b/src/stashbox/StashboxContainer.cs index d197544e..1d5b5ea8 100644 --- a/src/stashbox/StashboxContainer.cs +++ b/src/stashbox/StashboxContainer.cs @@ -19,7 +19,7 @@ namespace Stashbox /// /// Represents the stashbox dependency injection container. /// - public partial class StashboxContainer : ResolutionScopeBase, IStashboxContainer + public partial class StashboxContainer : IStashboxContainer { private readonly IContainerExtensionManager containerExtensionManager; private readonly IResolverSelector resolverSelector; @@ -45,6 +45,7 @@ public StashboxContainer(Action config = null) this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), configurator, new DecoratorRepository()); this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.RootScope = new ResolutionScope(this.activationContext); this.RegisterResolvers(); } @@ -60,6 +61,7 @@ internal StashboxContainer(IStashboxContainer parentContainer, IContainerExtensi this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), parentContainer.ContainerContext.ContainerConfigurator, parentContainer.ContainerContext.DecoratorRepository); this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.RootScope = new ResolutionScope(this.activationContext); this.containerExtensionManager.ReinitalizeExtensions(this.ContainerContext); } @@ -88,7 +90,7 @@ public void Validate() { foreach (var serviceRegistration in this.registrationRepository.GetAllRegistrations()) { - var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this), serviceRegistration.ServiceType); + var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this.RootScope), serviceRegistration.ServiceType); if (expression == null) throw new ResolutionFailedException(serviceRegistration.ImplementationType.FullName); } @@ -100,12 +102,15 @@ public void Validate() /// public IContainerContext ContainerContext { get; } + /// + public IResolutionScope RootScope { get; } + /// public IStashboxContainer CreateChildContainer() => new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector, this.expressionBuilder); /// - public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext); + public IResolutionScope BeginScope() => this.RootScope.BeginScope(); /// public void Configure(Action config) => @@ -128,23 +133,30 @@ public TTo BuildUp(TTo instance) where TTo : class var typeTo = instance.GetType(); var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, typeTo); - var resolutionInfo = ResolutionInfo.New(this); + var resolutionInfo = ResolutionInfo.New(this.RootScope); var expr = this.expressionBuilder.CreateFillExpression(this.containerExtensionManager, this.ContainerContext, Expression.Constant(instance), resolutionInfo, typeTo, null, metaInfoProvider.GetResolutionMembers(resolutionInfo), metaInfoProvider.GetResolutionMethods(resolutionInfo)); var factory = expr.CompileDelegate(Constants.ScopeExpression); - return factory(this) as TTo; + return factory(this.RootScope) as TTo; + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); } /// /// Disposes the container. /// /// Indicates the container is disposing or not. - protected override void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (!this.disposed.CompareExchange(false, true) || !disposing) return; - base.Dispose(true); + this.RootScope.Dispose(); this.registrationRepository.CleanUp(); this.containerExtensionManager.CleanUp(); } From c95b64c8dcface8a31b723d369f48a1dcc8bdfeb Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 02:29:37 +0100 Subject: [PATCH 20/27] Revert "Few fixes" This reverts commit 95de48d9face410af9ab2fc151a8fbd2b4d78e86. --- src/stashbox.tests/ContainerTests.cs | 108 ------------------ src/stashbox/Resolution/ResolutionStrategy.cs | 6 - 2 files changed, 114 deletions(-) diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index c27184bf..39282c33 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -127,102 +127,6 @@ public void ContainerTests_ResolverTest_SupportsMany() Assert.IsInstanceOfType(inst.First(), typeof(Test1)); } - [TestMethod] - public void ContainerTests_ResolutionScopeResolve() - { - var container = new StashboxContainer(); - var scope = container.Resolve(); - - Assert.IsNotNull(scope); - Assert.AreSame(container.RootScope, scope); - } - - [TestMethod] - public void ContainerTests_ResolutionScopeResolve_BeginScope() - { - var container = new StashboxContainer(); - var newScope = container.BeginScope(); - var scope = newScope.Resolve(); - - Assert.IsNotNull(scope); - Assert.AreSame(newScope, scope); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve() - { - var container = new StashboxContainer(); - var self = container.Resolve(); - - Assert.IsNotNull(self); - Assert.AreSame(container, self); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_BeginScope() - { - var container = new StashboxContainer(); - var newScope = container.BeginScope(); - var self = newScope.Resolve(); - - Assert.IsNotNull(self); - Assert.AreSame(container, self); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_CreateChild() - { - var container = new StashboxContainer(); - var newContainer = container.CreateChildContainer(); - var self = newContainer.Resolve(); - - Assert.IsNotNull(self); - Assert.AreSame(newContainer, self); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_Dependency_CreateChild() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var newContainer = container.CreateChildContainer(); - var inst = newContainer.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(newContainer, inst.Container); - } - - [TestMethod] - public void ContainerTests_ContainerSelfResolve_Dependency_BeginScope() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var scope = container.BeginScope(); - var inst = scope.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(container, inst.Container); - } - - [TestMethod] - public void ContainerTests_ResolutionScopeResolve_Dependency() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var inst = container.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(container.RootScope, inst.ResolutionScope); - } - - [TestMethod] - public void ContainerTests_ResolutionScopeResolve_Dependency_BeginScope() - { - var container = new StashboxContainer(config => config.WithUnknownTypeResolution()); - var newScope = container.BeginScope(); - var inst = newScope.Resolve(); - - Assert.IsNotNull(inst); - Assert.AreSame(newScope, inst.ResolutionScope); - } - public interface ITest1 { } public interface ITest2 { } @@ -255,18 +159,6 @@ public Test4(ITest3 test3) } } - public class Test5 - { - public IResolutionScope ResolutionScope { get; } - public IStashboxContainer Container { get; } - - public Test5(IResolutionScope resolutionScope, IStashboxContainer container) - { - ResolutionScope = resolutionScope; - Container = container; - } - } - public class TestResolver : Resolver { public override bool SupportsMany => true; diff --git a/src/stashbox/Resolution/ResolutionStrategy.cs b/src/stashbox/Resolution/ResolutionStrategy.cs index 8961c82f..6e687a02 100644 --- a/src/stashbox/Resolution/ResolutionStrategy.cs +++ b/src/stashbox/Resolution/ResolutionStrategy.cs @@ -18,12 +18,6 @@ internal ResolutionStrategy(IResolverSelector resolverSelector) public Expression BuildResolutionExpression(IContainerContext containerContext, ResolutionInfo resolutionInfo, TypeInformation typeInformation, InjectionParameter[] injectionParameters) { - if (typeInformation.Type == Constants.ResolutionScopeType) - return Expression.Constant(resolutionInfo.ResolutionScope); - - if (typeInformation.Type == Constants.StashboxContainerType) - return Expression.Constant(containerContext.Container); - if (resolutionInfo.ParameterExpressions != null && resolutionInfo.ParameterExpressions.Any(p => p.Type == typeInformation.Type)) return resolutionInfo.ParameterExpressions.Last(p => p.Type == typeInformation.Type); From f9cf534907f22e5ea17d333e7c5837a2377de526 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 02:29:41 +0100 Subject: [PATCH 21/27] Revert "Small refactor" This reverts commit d94fa7ae36cadcf8993692c1bdba57569212ad56. --- src/stashbox/Constants.cs | 4 ---- src/stashbox/Resolution/ActivationContext.cs | 22 +++++++------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/stashbox/Constants.cs b/src/stashbox/Constants.cs index 3ab365be..25a387de 100644 --- a/src/stashbox/Constants.cs +++ b/src/stashbox/Constants.cs @@ -26,9 +26,5 @@ public static class Constants internal static Type DisposableType = typeof(IDisposable); internal static Type FuncType = typeof(Func<>); - - internal static Type ResolutionScopeType = typeof(IResolutionScope); - - internal static Type StashboxContainerType = typeof(IStashboxContainer); } } diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index 8f9ef88c..c18630e6 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -22,7 +22,7 @@ public ActivationContext(IContainerContext containerContext, IResolverSelector r public object Activate(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) { var cachedFactory = this.containerContext.DelegateRepository.GetDelegateCacheOrDefault(type, name); - return cachedFactory != null ? cachedFactory(resolutionScope) : this.ActivateType(type, resolutionScope, name, nullResultAllowed); + return cachedFactory != null ? cachedFactory(resolutionScope) : this.Activate(ResolutionInfo.New(resolutionScope, nullResultAllowed), type, name); } public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) @@ -31,38 +31,32 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name, nullResultAllowed); } - private object ActivateType(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false) + private object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) { - if (type == Constants.ResolutionScopeType) - return resolutionScope; - - if (type == Constants.StashboxContainerType) - return this.containerContext.Container; - var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) { - var ragistrationFactory = registration.GetExpression(ResolutionInfo.New(resolutionScope, nullResultAllowed), type)?.CompileDelegate(Constants.ScopeExpression); + var ragistrationFactory = registration.GetExpression(resolutionInfo, type)?.CompileDelegate(Constants.ScopeExpression); if (ragistrationFactory == null) - if (nullResultAllowed) + if (resolutionInfo.NullResultAllowed) return null; else throw new ResolutionFailedException(type.FullName); this.containerContext.DelegateRepository.AddServiceDelegate(type, ragistrationFactory, name); - return ragistrationFactory(resolutionScope); + return ragistrationFactory(resolutionInfo.ResolutionScope); } - var expr = this.resolverSelector.GetResolverExpression(containerContext, new TypeInformation { Type = type, DependencyName = name }, ResolutionInfo.New(resolutionScope, nullResultAllowed)); + var expr = this.resolverSelector.GetResolverExpression(containerContext, new TypeInformation { Type = type, DependencyName = name }, resolutionInfo); if (expr == null) - if (nullResultAllowed) + if (resolutionInfo.NullResultAllowed) return null; else throw new ResolutionFailedException(type.FullName); var factory = expr.CompileDelegate(Constants.ScopeExpression); this.containerContext.DelegateRepository.AddServiceDelegate(type, factory, name); - return factory(resolutionScope); + return factory(resolutionInfo.ResolutionScope); } private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IResolutionScope resolutionScope, string name, bool nullResultAllowed) From 68c75cbe5837c63732caedef5d824127cc0848dd Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 02:29:45 +0100 Subject: [PATCH 22/27] Revert "Refactor" This reverts commit 2b326eec74244fd15e84aa3a58efab1704e6e4e4. --- src/stashbox.tests/AttributeTests.cs | 30 +++++++++ .../Infrastructure/IDependencyResolver.cs | 13 +++- .../Infrastructure/IResolutionScope.cs | 8 +-- .../Infrastructure/IStashboxContainer.cs | 13 +--- .../Resolution/IActivationContext.cs | 9 +++ src/stashbox/Resolution/ActivationContext.cs | 2 +- src/stashbox/ResolutionScope.cs | 63 ++++--------------- src/stashbox/ResolutionScopeBase.cs | 62 ++++++++++++++++++ src/stashbox/StashboxContainer.Resolver.cs | 10 +-- src/stashbox/StashboxContainer.cs | 31 ++++----- 10 files changed, 144 insertions(+), 97 deletions(-) create mode 100644 src/stashbox/ResolutionScopeBase.cs diff --git a/src/stashbox.tests/AttributeTests.cs b/src/stashbox.tests/AttributeTests.cs index 1d75f458..90d8276a 100644 --- a/src/stashbox.tests/AttributeTests.cs +++ b/src/stashbox.tests/AttributeTests.cs @@ -2,7 +2,10 @@ using Stashbox.Attributes; using Stashbox.Utils; using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Stashbox.Entity; namespace Stashbox.Tests { @@ -38,6 +41,33 @@ public void AttributeTests_Resolve() Assert.IsInstanceOfType(test3.test2, typeof(Test22)); } + [TestMethod] + public void AttributeTests_Resolve_Activator() + { + var container = new StashboxContainer(); + container.RegisterType("test1"); + container.RegisterType("test11"); + container.RegisterType("test12"); + + var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(ITest1), "test12"); + Assert.IsNotNull(inst); + Assert.IsInstanceOfType(inst, typeof(Test12)); + } + + [TestMethod] + public void AttributeTests_Resolve_Activator_Resolver() + { + var container = new StashboxContainer(); + container.RegisterType("test1"); + container.RegisterType("test11"); + container.RegisterType("test12"); + + var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(IEnumerable)); + Assert.IsNotNull(inst); + Assert.IsInstanceOfType(inst, typeof(IEnumerable)); + Assert.AreEqual(3, ((IEnumerable)inst).Count()); + } + [TestMethod] public void AttributeTests_Named_Resolution() { diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index d038f0ae..d52123b5 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -1,14 +1,20 @@ using Stashbox.Exceptions; using System; using System.Collections.Generic; +using Stashbox.Infrastructure.Resolution; namespace Stashbox.Infrastructure { /// /// Represents a dependency resolver. /// - public interface IDependencyResolver + public interface IDependencyResolver : IDisposable { + /// + /// The activation context. + /// + IActivationContext ActivationContext { get; } + /// /// Resolves an instance from the container. /// @@ -52,5 +58,10 @@ IEnumerable ResolveAll() /// The parameter type. /// The factory delegate. Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes); + + /// + /// Begins a new scope. + /// + IDependencyResolver BeginScope(); } } diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs index 22236ce3..798b697b 100644 --- a/src/stashbox/Infrastructure/IResolutionScope.cs +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -5,7 +5,7 @@ namespace Stashbox.Infrastructure /// /// Represents a resolution scope. /// - public interface IResolutionScope : IDependencyResolver, IDisposable + public interface IResolutionScope : IDisposable { /// /// Adds or updates an item in the scope. @@ -29,11 +29,5 @@ public interface IResolutionScope : IDependencyResolver, IDisposable /// The object. TDisposable AddDisposableTracking(TDisposable disposable) where TDisposable : IDisposable; - - /// - /// Begins a new scope. - /// - /// The new scope. - IResolutionScope BeginScope(); } } diff --git a/src/stashbox/Infrastructure/IStashboxContainer.cs b/src/stashbox/Infrastructure/IStashboxContainer.cs index cc398dc9..485e0d28 100644 --- a/src/stashbox/Infrastructure/IStashboxContainer.cs +++ b/src/stashbox/Infrastructure/IStashboxContainer.cs @@ -7,7 +7,7 @@ namespace Stashbox.Infrastructure /// /// Represents a dependency injection container. /// - public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator, IDisposable + public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolver, IDecoratorRegistrator { /// /// Registers a into the container. @@ -36,11 +36,6 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// IContainerContext ContainerContext { get; } - /// - /// The activation context. - /// - IResolutionScope RootScope { get; } - /// /// Checks a type can be resolved by the container. /// @@ -57,12 +52,6 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve /// True if the service can be resolved, otherwise false. bool CanResolve(Type typeFrom, string name = null); - /// - /// Begins a new scope. - /// - /// The new scope. - IResolutionScope BeginScope(); - /// /// Builds up an instance, the container will perform injections and extensions on it. /// diff --git a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs index 022856c8..77c67150 100644 --- a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs +++ b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs @@ -19,6 +19,15 @@ public interface IActivationContext /// The resolved object. object Activate(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false); + /// + /// Activates a type. + /// + /// The resolution info. + /// The type. + /// The service name. + /// The resolved object. + object Activate(ResolutionInfo resolutionInfo, Type type, string name = null); + /// /// Activates a type via a delegate. /// diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index c18630e6..fe97731f 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -31,7 +31,7 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name, nullResultAllowed); } - private object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) + public object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) { var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index cac7fa9c..ee5206fc 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -2,89 +2,50 @@ using System.Collections.Generic; using Stashbox.Infrastructure; using Stashbox.Infrastructure.Resolution; -using Stashbox.Utils; namespace Stashbox { /// /// Represents a resolution scope. /// - public class ResolutionScope : IResolutionScope + public class ResolutionScope : ResolutionScopeBase, IDependencyResolver { - private readonly IActivationContext activationContext; - private readonly AtomicBool disposed; - private readonly ConcurrentStore disposableObjects; - private readonly ConcurrentTree scopedItems; + /// + public IActivationContext ActivationContext { get; } /// - /// Constructs a . + /// Constructs a resolution scope. /// + /// The dependency resolver. public ResolutionScope(IActivationContext activationContext) { - this.activationContext = activationContext; - this.disposed = new AtomicBool(); - this.disposableObjects = new ConcurrentStore(); - this.scopedItems = new ConcurrentTree(); - } - - /// - public TDisposable AddDisposableTracking(TDisposable disposable) - where TDisposable : IDisposable - { - this.disposableObjects.Add(disposable); - return disposable; + this.ActivationContext = activationContext; } - /// - public void AddOrUpdateScopedItem(object key, object value) => - this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); - - /// - public object GetScopedItemOrDefault(object key) => - this.scopedItems.GetOrDefault(key); - /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; + this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); + this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.activationContext.Activate(type, this); + return (IEnumerable)this.ActivationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); /// - public IResolutionScope BeginScope() => new ResolutionScope(this.activationContext); - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes the scope. - /// - /// Indicates the scope is disposing or not. - protected virtual void Dispose(bool disposing) - { - if (!this.disposed.CompareExchange(false, true) || !disposing) return; - foreach (var disposableObject in disposableObjects) - disposableObject?.Dispose(); - } + public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); } } diff --git a/src/stashbox/ResolutionScopeBase.cs b/src/stashbox/ResolutionScopeBase.cs new file mode 100644 index 00000000..1e5d9ab4 --- /dev/null +++ b/src/stashbox/ResolutionScopeBase.cs @@ -0,0 +1,62 @@ +using System; +using Stashbox.Infrastructure; +using Stashbox.Utils; + +namespace Stashbox +{ + /// + /// Represents a base resolution scope. + /// + public class ResolutionScopeBase : IResolutionScope + { + private readonly AtomicBool disposed; + + private readonly ConcurrentStore disposableObjects; + + private readonly ConcurrentTree scopedItems; + + /// + /// Constructs a . + /// + public ResolutionScopeBase() + { + this.disposed = new AtomicBool(); + this.disposableObjects = new ConcurrentStore(); + this.scopedItems = new ConcurrentTree(); + } + + /// + public TDisposable AddDisposableTracking(TDisposable disposable) + where TDisposable : IDisposable + { + this.disposableObjects.Add(disposable); + return disposable; + } + + /// + public void AddOrUpdateScopedItem(object key, object value) => + this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); + + /// + public object GetScopedItemOrDefault(object key) => + this.scopedItems.GetOrDefault(key); + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the scope. + /// + /// Indicates the scope is disposing or not. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed.CompareExchange(false, true) || !disposing) return; + foreach (var disposableObject in disposableObjects) + disposableObject?.Dispose(); + } + } +} diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index ea2114f3..04e4b004 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -8,26 +8,26 @@ public partial class StashboxContainer { /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.activationContext.Activate(typeof(TKey), this.RootScope, name, nullResultAllowed) as TKey; + this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.activationContext.Activate(typeFrom, this.RootScope, name, nullResultAllowed); + this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.activationContext.Activate(typeof(IEnumerable), this.RootScope) as IEnumerable; + this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { Shield.EnsureNotNull(typeFrom, nameof(typeFrom)); var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.activationContext.Activate(type, this.RootScope); + return (IEnumerable)this.ActivationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.activationContext.ActivateFactory(typeFrom, parameterTypes, this.RootScope, name, nullResultAllowed); + this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); } } diff --git a/src/stashbox/StashboxContainer.cs b/src/stashbox/StashboxContainer.cs index 1d5b5ea8..d209208d 100644 --- a/src/stashbox/StashboxContainer.cs +++ b/src/stashbox/StashboxContainer.cs @@ -19,13 +19,12 @@ namespace Stashbox /// /// Represents the stashbox dependency injection container. /// - public partial class StashboxContainer : IStashboxContainer + public partial class StashboxContainer : ResolutionScopeBase, IStashboxContainer { private readonly IContainerExtensionManager containerExtensionManager; private readonly IResolverSelector resolverSelector; private readonly IRegistrationRepository registrationRepository; private readonly IExpressionBuilder expressionBuilder; - private readonly IActivationContext activationContext; private readonly AtomicBool disposed; /// @@ -44,8 +43,8 @@ public StashboxContainer(Action config = null) this.registrationRepository = new RegistrationRepository(configurator); this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), configurator, new DecoratorRepository()); - this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); - this.RootScope = new ResolutionScope(this.activationContext); + this.ActivationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.RegisterResolvers(); } @@ -60,8 +59,7 @@ internal StashboxContainer(IStashboxContainer parentContainer, IContainerExtensi this.registrationRepository = new RegistrationRepository(parentContainer.ContainerContext.ContainerConfigurator); this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), parentContainer.ContainerContext.ContainerConfigurator, parentContainer.ContainerContext.DecoratorRepository); - this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); - this.RootScope = new ResolutionScope(this.activationContext); + this.ActivationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); this.containerExtensionManager.ReinitalizeExtensions(this.ContainerContext); } @@ -90,7 +88,7 @@ public void Validate() { foreach (var serviceRegistration in this.registrationRepository.GetAllRegistrations()) { - var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this.RootScope), serviceRegistration.ServiceType); + var expression = serviceRegistration.GetExpression(ResolutionInfo.New(this), serviceRegistration.ServiceType); if (expression == null) throw new ResolutionFailedException(serviceRegistration.ImplementationType.FullName); } @@ -103,14 +101,14 @@ public void Validate() public IContainerContext ContainerContext { get; } /// - public IResolutionScope RootScope { get; } + public IActivationContext ActivationContext { get; } /// public IStashboxContainer CreateChildContainer() => new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector, this.expressionBuilder); /// - public IResolutionScope BeginScope() => this.RootScope.BeginScope(); + public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); /// public void Configure(Action config) => @@ -133,30 +131,23 @@ public TTo BuildUp(TTo instance) where TTo : class var typeTo = instance.GetType(); var metaInfoProvider = new MetaInfoProvider(this.ContainerContext, RegistrationContextData.Empty, typeTo); - var resolutionInfo = ResolutionInfo.New(this.RootScope); + var resolutionInfo = ResolutionInfo.New(this); var expr = this.expressionBuilder.CreateFillExpression(this.containerExtensionManager, this.ContainerContext, Expression.Constant(instance), resolutionInfo, typeTo, null, metaInfoProvider.GetResolutionMembers(resolutionInfo), metaInfoProvider.GetResolutionMethods(resolutionInfo)); var factory = expr.CompileDelegate(Constants.ScopeExpression); - return factory(this.RootScope) as TTo; - } - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); + return factory(this) as TTo; } /// /// Disposes the container. /// /// Indicates the container is disposing or not. - protected virtual void Dispose(bool disposing) + protected override void Dispose(bool disposing) { if (!this.disposed.CompareExchange(false, true) || !disposing) return; - this.RootScope.Dispose(); + base.Dispose(true); this.registrationRepository.CleanUp(); this.containerExtensionManager.CleanUp(); } From 431b2bbf7193e16daba4cb4ec9386e3255be48da Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 03:05:30 +0100 Subject: [PATCH 23/27] Refactor --- src/stashbox.tests/AttributeTests.cs | 29 +------------------ src/stashbox.tests/LifetimeTests.cs | 2 +- src/stashbox/ContainerContext.cs | 1 - .../Infrastructure/IDependencyResolver.cs | 6 ---- .../Resolution/IActivationContext.cs | 14 ++------- src/stashbox/Lifetime/LifetimeBase.cs | 19 ++++++++++-- src/stashbox/Lifetime/ScopedLifetime.cs | 8 +---- src/stashbox/Lifetime/SingletonLifetime.cs | 5 +--- .../Registration/RegistrationRepository.cs | 2 +- .../Registration/ServiceRegistration.cs | 19 ++++++------ src/stashbox/Resolution/ActivationContext.cs | 2 +- src/stashbox/ResolutionScope.cs | 17 +++++------ src/stashbox/ResolutionScopeBase.cs | 2 -- src/stashbox/StashboxContainer.Resolver.cs | 11 ++++--- src/stashbox/StashboxContainer.cs | 10 +++---- 15 files changed, 52 insertions(+), 95 deletions(-) diff --git a/src/stashbox.tests/AttributeTests.cs b/src/stashbox.tests/AttributeTests.cs index 90d8276a..3bda47c9 100644 --- a/src/stashbox.tests/AttributeTests.cs +++ b/src/stashbox.tests/AttributeTests.cs @@ -40,34 +40,7 @@ public void AttributeTests_Resolve() Assert.IsInstanceOfType(test3.test1, typeof(Test11)); Assert.IsInstanceOfType(test3.test2, typeof(Test22)); } - - [TestMethod] - public void AttributeTests_Resolve_Activator() - { - var container = new StashboxContainer(); - container.RegisterType("test1"); - container.RegisterType("test11"); - container.RegisterType("test12"); - - var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(ITest1), "test12"); - Assert.IsNotNull(inst); - Assert.IsInstanceOfType(inst, typeof(Test12)); - } - - [TestMethod] - public void AttributeTests_Resolve_Activator_Resolver() - { - var container = new StashboxContainer(); - container.RegisterType("test1"); - container.RegisterType("test11"); - container.RegisterType("test12"); - - var inst = container.ActivationContext.Activate(ResolutionInfo.New(container), typeof(IEnumerable)); - Assert.IsNotNull(inst); - Assert.IsInstanceOfType(inst, typeof(IEnumerable)); - Assert.AreEqual(3, ((IEnumerable)inst).Count()); - } - + [TestMethod] public void AttributeTests_Named_Resolution() { diff --git a/src/stashbox.tests/LifetimeTests.cs b/src/stashbox.tests/LifetimeTests.cs index b7ec0106..24dc0554 100644 --- a/src/stashbox.tests/LifetimeTests.cs +++ b/src/stashbox.tests/LifetimeTests.cs @@ -80,7 +80,7 @@ public void LifetimeTests_Resolve_Parallel_Lazy() public void LifetimeTests_StateCheck() { var scoped = new ScopedLifetime(); - Assert.IsTrue(scoped.HandlesObjectDisposal); + Assert.IsFalse(scoped.HandlesObjectDisposal); Assert.IsInstanceOfType(scoped.Create(), typeof(ScopedLifetime)); var singleton = new SingletonLifetime(); diff --git a/src/stashbox/ContainerContext.cs b/src/stashbox/ContainerContext.cs index dec08b92..214068ee 100644 --- a/src/stashbox/ContainerContext.cs +++ b/src/stashbox/ContainerContext.cs @@ -1,5 +1,4 @@ using Stashbox.Infrastructure; -using Stashbox.Registration; using Stashbox.Utils; using System.Threading; using Stashbox.Infrastructure.Registration; diff --git a/src/stashbox/Infrastructure/IDependencyResolver.cs b/src/stashbox/Infrastructure/IDependencyResolver.cs index d52123b5..d3f193b1 100644 --- a/src/stashbox/Infrastructure/IDependencyResolver.cs +++ b/src/stashbox/Infrastructure/IDependencyResolver.cs @@ -1,7 +1,6 @@ using Stashbox.Exceptions; using System; using System.Collections.Generic; -using Stashbox.Infrastructure.Resolution; namespace Stashbox.Infrastructure { @@ -10,11 +9,6 @@ namespace Stashbox.Infrastructure /// public interface IDependencyResolver : IDisposable { - /// - /// The activation context. - /// - IActivationContext ActivationContext { get; } - /// /// Resolves an instance from the container. /// diff --git a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs index 77c67150..42e66b9c 100644 --- a/src/stashbox/Infrastructure/Resolution/IActivationContext.cs +++ b/src/stashbox/Infrastructure/Resolution/IActivationContext.cs @@ -1,5 +1,4 @@ -using Stashbox.Entity; -using System; +using System; using Stashbox.Exceptions; namespace Stashbox.Infrastructure.Resolution @@ -18,16 +17,7 @@ public interface IActivationContext /// If true, the container will return with null instead of throwing . /// The resolved object. object Activate(Type type, IResolutionScope resolutionScope, string name = null, bool nullResultAllowed = false); - - /// - /// Activates a type. - /// - /// The resolution info. - /// The type. - /// The service name. - /// The resolved object. - object Activate(ResolutionInfo resolutionInfo, Type type, string name = null); - + /// /// Activates a type via a delegate. /// diff --git a/src/stashbox/Lifetime/LifetimeBase.cs b/src/stashbox/Lifetime/LifetimeBase.cs index 373112f0..ca445277 100644 --- a/src/stashbox/Lifetime/LifetimeBase.cs +++ b/src/stashbox/Lifetime/LifetimeBase.cs @@ -1,7 +1,9 @@ using System; +using System.Linq; using Stashbox.Entity; using Stashbox.Infrastructure; using System.Linq.Expressions; +using System.Reflection; namespace Stashbox.Lifetime { @@ -15,8 +17,21 @@ public abstract class LifetimeBase : ILifetime /// public virtual Expression GetExpression(IContainerContext containerContext, IObjectBuilder objectBuilder, - ResolutionInfo resolutionInfo, Type resolveType) => - objectBuilder.GetExpression(resolutionInfo, resolveType); + ResolutionInfo resolutionInfo, Type resolveType) + + { + var expr = objectBuilder.GetExpression(resolutionInfo, resolveType); + + if (expr == null) + return null; + + if (!expr.Type.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType) || + objectBuilder.HandlesObjectDisposal || this.HandlesObjectDisposal) + return expr; + + var method = Constants.AddDisposalMethod.MakeGenericMethod(expr.Type); + return Expression.Call(Constants.ScopeExpression, method, expr); + } /// public abstract ILifetime Create(); diff --git a/src/stashbox/Lifetime/ScopedLifetime.cs b/src/stashbox/Lifetime/ScopedLifetime.cs index c5cfaf0e..b526a488 100644 --- a/src/stashbox/Lifetime/ScopedLifetime.cs +++ b/src/stashbox/Lifetime/ScopedLifetime.cs @@ -48,10 +48,7 @@ public override Expression GetExpression(IContainerContext containerContext, IOb return this.expression; } - - /// - public override bool HandlesObjectDisposal => true; - + private static TValue GetScopedValue(IResolutionScope scope, Func factory, string scopeId) { var value = scope.GetScopedItemOrDefault(scopeId); @@ -59,9 +56,6 @@ private static TValue GetScopedValue(IResolutionScope scope, Func GetRegistrationsOrDefault(Type type) var conditional = this.GetRegistrationsOrDefault(type, this.conditionalRepository); var registrations = this.GetRegistrationsOrDefault(type, this.serviceRepository); - return conditional != null ? conditional.Concat(registrations) : registrations; + return conditional?.Concat(registrations) ?? registrations; } public IEnumerable GetAllRegistrations() => diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index 87ea7dd0..07c83e0c 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -71,7 +71,7 @@ public bool IsUsableForCurrentContext(TypeInformation typeInfo) => this.TargetTy /// public bool ValidateGenericContraints(Type type) => !this.metaInfoProvider.HasGenericTypeConstraints || this.metaInfoProvider.ValidateGenericContraints(type); - + /// public void CleanUp() { @@ -82,21 +82,22 @@ public void CleanUp() /// public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) { - var expr = this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? + var expr = this.LifetimeManager == null || this.ServiceType.IsOpenGenericType() ? this.ObjectBuilder.GetExpression(resolutionInfo, resolveType) : this.LifetimeManager.GetExpression(this.containerContext, this.ObjectBuilder, resolutionInfo, resolveType); if (expr == null) return null; - if (!this.containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled || - this.LifetimeManager != null && this.LifetimeManager.HandlesObjectDisposal || - this.ObjectBuilder.HandlesObjectDisposal || - !this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) return expr; - - var method = Constants.AddDisposalMethod.MakeGenericMethod(this.ImplementationType); + if (this.LifetimeManager == null && !this.ObjectBuilder.HandlesObjectDisposal && + this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) + { + var method = Constants.AddDisposalMethod.MakeGenericMethod(this.ImplementationType); + return Expression.Call(Constants.ScopeExpression, method, + Expression.Convert(expr, this.ImplementationType)); + } - return Expression.Call(Constants.ScopeExpression, method, Expression.Convert(expr, this.ImplementationType)); + return expr; } } } diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index fe97731f..c18630e6 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -31,7 +31,7 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco return cachedFactory != null ? cachedFactory(resolutionScope) : ActivateFactoryDelegate(type, parameterTypes, resolutionScope, name, nullResultAllowed); } - public object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) + private object Activate(ResolutionInfo resolutionInfo, Type type, string name = null) { var registration = this.containerContext.RegistrationRepository.GetRegistrationOrDefault(type, name); if (registration != null) diff --git a/src/stashbox/ResolutionScope.cs b/src/stashbox/ResolutionScope.cs index ee5206fc..c9a78782 100644 --- a/src/stashbox/ResolutionScope.cs +++ b/src/stashbox/ResolutionScope.cs @@ -10,8 +10,7 @@ namespace Stashbox /// public class ResolutionScope : ResolutionScopeBase, IDependencyResolver { - /// - public IActivationContext ActivationContext { get; } + private readonly IActivationContext activationContext; /// /// Constructs a resolution scope. @@ -19,33 +18,33 @@ public class ResolutionScope : ResolutionScopeBase, IDependencyResolver /// The dependency resolver. public ResolutionScope(IActivationContext activationContext) { - this.ActivationContext = activationContext; + this.activationContext = activationContext; } /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; + this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); + this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.ActivationContext.Activate(type, this); + return (IEnumerable)this.activationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); /// - public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); + public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext); } } diff --git a/src/stashbox/ResolutionScopeBase.cs b/src/stashbox/ResolutionScopeBase.cs index 1e5d9ab4..ef12bef3 100644 --- a/src/stashbox/ResolutionScopeBase.cs +++ b/src/stashbox/ResolutionScopeBase.cs @@ -10,9 +10,7 @@ namespace Stashbox public class ResolutionScopeBase : IResolutionScope { private readonly AtomicBool disposed; - private readonly ConcurrentStore disposableObjects; - private readonly ConcurrentTree scopedItems; /// diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index 04e4b004..6ef7762c 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -8,26 +8,25 @@ public partial class StashboxContainer { /// public TKey Resolve(string name = null, bool nullResultAllowed = false) where TKey : class => - this.ActivationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; + this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed) as TKey; /// public object Resolve(Type typeFrom, string name = null, bool nullResultAllowed = false) => - this.ActivationContext.Activate(typeFrom, this, name, nullResultAllowed); + this.activationContext.Activate(typeFrom, this, name, nullResultAllowed); /// public IEnumerable ResolveAll() where TKey : class => - this.ActivationContext.Activate(typeof(IEnumerable), this) as IEnumerable; + this.activationContext.Activate(typeof(IEnumerable), this) as IEnumerable; /// public IEnumerable ResolveAll(Type typeFrom) { - Shield.EnsureNotNull(typeFrom, nameof(typeFrom)); var type = typeof(IEnumerable<>).MakeGenericType(typeFrom); - return (IEnumerable)this.ActivationContext.Activate(type, this); + return (IEnumerable)this.activationContext.Activate(type, this); } /// public Delegate ResolveFactory(Type typeFrom, string name = null, bool nullResultAllowed = false, params Type[] parameterTypes) => - this.ActivationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); + this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed); } } diff --git a/src/stashbox/StashboxContainer.cs b/src/stashbox/StashboxContainer.cs index d209208d..d3fad437 100644 --- a/src/stashbox/StashboxContainer.cs +++ b/src/stashbox/StashboxContainer.cs @@ -26,6 +26,7 @@ public partial class StashboxContainer : ResolutionScopeBase, IStashboxContainer private readonly IRegistrationRepository registrationRepository; private readonly IExpressionBuilder expressionBuilder; private readonly AtomicBool disposed; + private readonly IActivationContext activationContext; /// /// Constructs a @@ -43,7 +44,7 @@ public StashboxContainer(Action config = null) this.registrationRepository = new RegistrationRepository(configurator); this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), configurator, new DecoratorRepository()); - this.ActivationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); this.RegisterResolvers(); } @@ -59,7 +60,7 @@ internal StashboxContainer(IStashboxContainer parentContainer, IContainerExtensi this.registrationRepository = new RegistrationRepository(parentContainer.ContainerContext.ContainerConfigurator); this.ContainerContext = new ContainerContext(this.registrationRepository, new DelegateRepository(), this, new ResolutionStrategy(this.resolverSelector), parentContainer.ContainerContext.ContainerConfigurator, parentContainer.ContainerContext.DecoratorRepository); - this.ActivationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); + this.activationContext = new Resolution.ActivationContext(this.ContainerContext, this.resolverSelector); this.containerExtensionManager.ReinitalizeExtensions(this.ContainerContext); } @@ -100,15 +101,12 @@ public void Validate() /// public IContainerContext ContainerContext { get; } - /// - public IActivationContext ActivationContext { get; } - /// public IStashboxContainer CreateChildContainer() => new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector, this.expressionBuilder); /// - public IDependencyResolver BeginScope() => new ResolutionScope(this.ActivationContext); + public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext); /// public void Configure(Action config) => From 43b4cb4eeb7c700f632e5f4235e0688b435506e5 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 03:24:07 +0100 Subject: [PATCH 24/27] Few changes --- src/stashbox/Lifetime/SingletonLifetime.cs | 6 +++++- src/stashbox/Registration/ServiceRegistration.cs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/stashbox/Lifetime/SingletonLifetime.cs b/src/stashbox/Lifetime/SingletonLifetime.cs index 43839d26..960bcdda 100644 --- a/src/stashbox/Lifetime/SingletonLifetime.cs +++ b/src/stashbox/Lifetime/SingletonLifetime.cs @@ -25,7 +25,11 @@ public override Expression GetExpression(IContainerContext containerContext, IOb if (expr == null) return null; - this.instance = expr.CompileDelegate(Constants.ScopeExpression)(resolutionInfo.ResolutionScope); + if (expr.NodeType == ExpressionType.New && ((NewExpression)expr).Arguments.Count == 0) + this.instance = Activator.CreateInstance(expr.Type); + else + this.instance = expr.CompileDelegate(Constants.ScopeExpression)(resolutionInfo.ResolutionScope); + this.expression = Expression.Constant(this.instance); } diff --git a/src/stashbox/Registration/ServiceRegistration.cs b/src/stashbox/Registration/ServiceRegistration.cs index 07c83e0c..c56bae72 100644 --- a/src/stashbox/Registration/ServiceRegistration.cs +++ b/src/stashbox/Registration/ServiceRegistration.cs @@ -89,8 +89,8 @@ public Expression GetExpression(ResolutionInfo resolutionInfo, Type resolveType) if (expr == null) return null; - if (this.LifetimeManager == null && !this.ObjectBuilder.HandlesObjectDisposal && - this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) + if (this.LifetimeManager == null && containerContext.ContainerConfigurator.ContainerConfiguration.TrackTransientsForDisposalEnabled && + !this.ObjectBuilder.HandlesObjectDisposal && this.ImplementationType.GetTypeInfo().ImplementedInterfaces.Contains(Constants.DisposableType)) { var method = Constants.AddDisposalMethod.MakeGenericMethod(this.ImplementationType); return Expression.Call(Constants.ScopeExpression, method, From 2b0d1e1de59d4f95d277c69e0840d05dab990c43 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 11:16:37 +0100 Subject: [PATCH 25/27] Added more unit tests --- src/stashbox.tests/ContainerTests.cs | 2 +- src/stashbox.tests/EnumerableTests.cs | 156 +++++++++++++++++++-- src/stashbox/StashboxContainer.Resolver.cs | 3 +- 3 files changed, 149 insertions(+), 12 deletions(-) diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index 39282c33..71f9fdeb 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -36,7 +36,7 @@ public void ContainerTests_ChildContainer_ResolveFromParent() var container = new StashboxContainer(); container.RegisterType(); - var child = container.BeginScope(); + var child = container.CreateChildContainer(); var test1 = child.Resolve(); diff --git a/src/stashbox.tests/EnumerableTests.cs b/src/stashbox.tests/EnumerableTests.cs index f4ab3806..3ef46f2d 100644 --- a/src/stashbox.tests/EnumerableTests.cs +++ b/src/stashbox.tests/EnumerableTests.cs @@ -110,7 +110,7 @@ public void EnumerableTests_Resolve_Null() } [TestMethod] - public void EnumerableTests_Resolve_Parent() + public void EnumerableTests_Resolve_Scoped() { IStashboxContainer container = new StashboxContainer(); container.RegisterType(); @@ -125,7 +125,22 @@ public void EnumerableTests_Resolve_Parent() } [TestMethod] - public void EnumerableTests_Resolve_Parent_Null() + public void EnumerableTests_Resolve_Parent() + { + IStashboxContainer container = new StashboxContainer(); + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + + var child = container.CreateChildContainer(); + + var all = child.Resolve>(); + + Assert.AreEqual(3, all.Count()); + } + + [TestMethod] + public void EnumerableTests_Resolve_Scoped_Null() { IStashboxContainer container = new StashboxContainer(); @@ -137,7 +152,19 @@ public void EnumerableTests_Resolve_Parent_Null() } [TestMethod] - public void EnumerableTests_Resolve_Parent_Lazy() + public void EnumerableTests_Resolve_Parent_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var child = container.CreateChildContainer(); + + var all = child.Resolve>(); + + Assert.AreEqual(0, all.Count()); + } + + [TestMethod] + public void EnumerableTests_Resolve_Scoped_Lazy() { IStashboxContainer container = new StashboxContainer(); container.RegisterType(); @@ -152,7 +179,22 @@ public void EnumerableTests_Resolve_Parent_Lazy() } [TestMethod] - public void EnumerableTests_Resolve_Parent_Lazy_Null() + public void EnumerableTests_Resolve_Parent_Lazy() + { + IStashboxContainer container = new StashboxContainer(); + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + + var child = container.CreateChildContainer(); + + var all = child.Resolve>>(); + + Assert.AreEqual(3, all.Count()); + } + + [TestMethod] + public void EnumerableTests_Resolve_Scoped_Lazy_Null() { IStashboxContainer container = new StashboxContainer(); @@ -164,7 +206,19 @@ public void EnumerableTests_Resolve_Parent_Lazy_Null() } [TestMethod] - public void EnumerableTests_Resolve_Parent_Func() + public void EnumerableTests_Resolve_Parent_Lazy_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var child = container.CreateChildContainer(); + + var all = child.Resolve>>(); + + Assert.AreEqual(0, all.Count()); + } + + [TestMethod] + public void EnumerableTests_Resolve_Scoped_Func() { IStashboxContainer container = new StashboxContainer(); container.RegisterType(); @@ -179,7 +233,22 @@ public void EnumerableTests_Resolve_Parent_Func() } [TestMethod] - public void EnumerableTests_Resolve_Parent_Func_Null() + public void EnumerableTests_Resolve_Parent_Func() + { + IStashboxContainer container = new StashboxContainer(); + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + + var child = container.CreateChildContainer(); + + var all = child.Resolve>>(); + + Assert.AreEqual(3, all.Count()); + } + + [TestMethod] + public void EnumerableTests_Resolve_Scoped_Func_Null() { IStashboxContainer container = new StashboxContainer(); @@ -190,6 +259,18 @@ public void EnumerableTests_Resolve_Parent_Func_Null() Assert.AreEqual(0, all.Count()); } + [TestMethod] + public void EnumerableTests_Resolve_Parent_Func_Null() + { + IStashboxContainer container = new StashboxContainer(); + + var child = container.CreateChildContainer(); + + var all = child.Resolve>>(); + + Assert.AreEqual(0, all.Count()); + } + [TestMethod] public void EnumerableTests_Resolve_Lazy() { @@ -332,7 +413,7 @@ public void EnumerableTests_ResolveAll_PreserveOrder() } [TestMethod] - public void EnumerableTests_Resolve_PreserveOrder_Parent() + public void EnumerableTests_Resolve_PreserveOrder_Scoped() { IStashboxContainer container = new StashboxContainer(config => config.WithEnumerableOrderRule(Rules.EnumerableOrder.PreserveOrder)); @@ -351,7 +432,26 @@ public void EnumerableTests_Resolve_PreserveOrder_Parent() } [TestMethod] - public void EnumerableTests_Resolve_PreserveOrder_Parent_Lazy() + public void EnumerableTests_Resolve_PreserveOrder_Parent() + { + IStashboxContainer container = new StashboxContainer(config => + config.WithEnumerableOrderRule(Rules.EnumerableOrder.PreserveOrder)); + + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + + var child = container.CreateChildContainer(); + + var services = child.Resolve>().ToArray(); + + Assert.IsInstanceOfType(services[0], typeof(Test1)); + Assert.IsInstanceOfType(services[1], typeof(Test11)); + Assert.IsInstanceOfType(services[2], typeof(Test12)); + } + + [TestMethod] + public void EnumerableTests_Resolve_PreserveOrder_Scoped_Lazy() { IStashboxContainer container = new StashboxContainer(config => config.WithEnumerableOrderRule(Rules.EnumerableOrder.PreserveOrder)); @@ -370,7 +470,26 @@ public void EnumerableTests_Resolve_PreserveOrder_Parent_Lazy() } [TestMethod] - public void EnumerableTests_Resolve_PreserveOrder_Parent_Func() + public void EnumerableTests_Resolve_PreserveOrder_Parent_Lazy() + { + IStashboxContainer container = new StashboxContainer(config => + config.WithEnumerableOrderRule(Rules.EnumerableOrder.PreserveOrder)); + + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + + var child = container.CreateChildContainer(); + + var services = child.Resolve>>().ToArray(); + + Assert.IsInstanceOfType(services[0].Value, typeof(Test1)); + Assert.IsInstanceOfType(services[1].Value, typeof(Test11)); + Assert.IsInstanceOfType(services[2].Value, typeof(Test12)); + } + + [TestMethod] + public void EnumerableTests_Resolve_PreserveOrder_Scoped_Func() { IStashboxContainer container = new StashboxContainer(config => config.WithEnumerableOrderRule(Rules.EnumerableOrder.PreserveOrder)); @@ -388,6 +507,25 @@ public void EnumerableTests_Resolve_PreserveOrder_Parent_Func() Assert.IsInstanceOfType(services[2](), typeof(Test12)); } + [TestMethod] + public void EnumerableTests_Resolve_PreserveOrder_Parent_Func() + { + IStashboxContainer container = new StashboxContainer(config => + config.WithEnumerableOrderRule(Rules.EnumerableOrder.PreserveOrder)); + + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + + var child = container.CreateChildContainer(); + + var services = child.Resolve>>().ToArray(); + + Assert.IsInstanceOfType(services[0](), typeof(Test1)); + Assert.IsInstanceOfType(services[1](), typeof(Test11)); + Assert.IsInstanceOfType(services[2](), typeof(Test12)); + } + [TestMethod] public void EnumerableTests_Resolve_PreserveOrder_Lazy() { diff --git a/src/stashbox/StashboxContainer.Resolver.cs b/src/stashbox/StashboxContainer.Resolver.cs index 6ef7762c..893bd111 100644 --- a/src/stashbox/StashboxContainer.Resolver.cs +++ b/src/stashbox/StashboxContainer.Resolver.cs @@ -1,5 +1,4 @@ -using Stashbox.Utils; -using System; +using System; using System.Collections.Generic; namespace Stashbox From e684ae4d241bc634c770a095bce96d8bb37e9f23 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 13:51:09 +0100 Subject: [PATCH 26/27] Added more unit tests --- src/stashbox.tests/ContainerTests.cs | 11 +++ src/stashbox.tests/DecoratorTests.cs | 52 ++++++++++++++ src/stashbox.tests/DisposeTests.cs | 66 ++++++++++++++++-- src/stashbox.tests/EnumerableTests.cs | 52 ++++++++++---- src/stashbox.tests/LazyTests.cs | 17 +++++ src/stashbox.tests/ResolverTests.cs | 16 ++++- src/stashbox.tests/StandardResolveTests.cs | 72 +++++++++++++++++++- src/stashbox.tests/TupleTests.cs | 10 +++ src/stashbox/Resolution/ActivationContext.cs | 5 +- 9 files changed, 277 insertions(+), 24 deletions(-) diff --git a/src/stashbox.tests/ContainerTests.cs b/src/stashbox.tests/ContainerTests.cs index 71f9fdeb..ca4a6863 100644 --- a/src/stashbox.tests/ContainerTests.cs +++ b/src/stashbox.tests/ContainerTests.cs @@ -67,6 +67,17 @@ public void ContainerTests_Validate_CircularDependency() } } + [TestMethod] + public void ContainerTests_Validate_Ok() + { + using (var container = new StashboxContainer()) + { + container.RegisterType(); + container.RegisterType(); + container.Validate(); + } + } + [TestMethod] public void ContainerTests_CheckRegistration() { diff --git a/src/stashbox.tests/DecoratorTests.cs b/src/stashbox.tests/DecoratorTests.cs index f22cdf6e..334cd344 100644 --- a/src/stashbox.tests/DecoratorTests.cs +++ b/src/stashbox.tests/DecoratorTests.cs @@ -333,8 +333,40 @@ public void DecoratorTests_OpenGeneric() } } + [TestMethod] + public void DecoratorTests_DecoratorDependency_Null() + { + using (var container = new StashboxContainer()) + { + container.RegisterType(); + container.RegisterDecorator(); + + var inst = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(inst); + } + } + + [TestMethod] + public void DecoratorTests_DecoreteeDependency_Null() + { + using (var container = new StashboxContainer()) + { + container.RegisterType(); + container.RegisterDecorator(); + + var inst = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(inst); + } + } + public interface ITest1 { ITest1 Test { get; } } + public interface IDecoratorDep { } + + public interface IDep { } + public interface ITest1 { ITest1 Test { get; } } public class Test1 : ITest1 @@ -347,6 +379,16 @@ public class Test11 : ITest1 public ITest1 Test { get; } } + public class Test12 : ITest1 + { + public ITest1 Test { get; } + + public Test12(IDep dep) + { + + } + } + public class Test1 : ITest1 { public ITest1 Test { get; } @@ -437,5 +479,15 @@ public TestDecorator8(IEnumerable test1) this.Test = test1.First(); } } + + public class TestDecorator9 : ITest1 + { + public ITest1 Test { get; } + + public TestDecorator9(ITest1 test1, IDecoratorDep dep) + { + this.Test = test1; + } + } } } diff --git a/src/stashbox.tests/DisposeTests.cs b/src/stashbox.tests/DisposeTests.cs index 82c1688c..8a0138e0 100644 --- a/src/stashbox.tests/DisposeTests.cs +++ b/src/stashbox.tests/DisposeTests.cs @@ -164,6 +164,51 @@ public void DisposeTests_TrackTransientDisposal_Scoped_Transient() Assert.IsTrue(test3.Test1.Disposed); } + [TestMethod] + public void DisposeTests_TrackTransientDisposal_ScopeOfScope_Transient() + { + using (IStashboxContainer container = new StashboxContainer(config => config.WithDisposableTransientTracking())) + { + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + + ITest1 test; + ITest2 test2; + Test3 test3; + + using (var scope = container.BeginScope()) + { + test = scope.Resolve(); + test2 = scope.Resolve(); + test3 = scope.Resolve(); + + ITest1 test4; + ITest2 test5; + Test3 test6; + + using (var scope2 = scope.BeginScope()) + { + test4 = scope2.Resolve(); + test5 = scope2.Resolve(); + test6 = scope2.Resolve(); + } + + Assert.IsTrue(test4.Disposed); + Assert.IsTrue(test5.Test1.Disposed); + Assert.IsTrue(test6.Test1.Disposed); + + Assert.IsFalse(test.Disposed); + Assert.IsFalse(test2.Test1.Disposed); + Assert.IsFalse(test3.Test1.Disposed); + } + + Assert.IsTrue(test.Disposed); + Assert.IsTrue(test2.Test1.Disposed); + Assert.IsTrue(test3.Test1.Disposed); + } + } + [TestMethod] public void DisposeTests_TrackTransientDisposal_Scoped_Transient_ChildContainer() { @@ -176,11 +221,12 @@ public void DisposeTests_TrackTransientDisposal_Scoped_Transient_ChildContainer( container.RegisterType(); container.RegisterType(); - using (var child = container.BeginScope()) + using (var child = container.CreateChildContainer()) + using (var scope = child.BeginScope()) { - test4 = child.Resolve(); - test5 = child.Resolve(); - test6 = child.Resolve(); + test4 = scope.Resolve(); + test5 = scope.Resolve(); + test6 = scope.Resolve(); } Assert.IsTrue(test4.Disposed); @@ -245,7 +291,11 @@ public void DisposeTests_Instance_TrackTransient() { ITest1 test = new Test1(); using (var container = new StashboxContainer(config => config.WithDisposableTransientTracking())) - container.RegisterInstance(test); + { + container.RegisterInstance(test); + + Assert.AreSame(test, container.Resolve()); + } Assert.IsTrue(test.Disposed); } @@ -255,7 +305,11 @@ public void DisposeTests_WireUp_TrackTransient() { ITest1 test = new Test1(); using (var container = new StashboxContainer(config => config.WithDisposableTransientTracking())) - container.WireUp(test); + { + container.WireUp(test); + + Assert.AreSame(test, container.Resolve()); + } Assert.IsTrue(test.Disposed); } diff --git a/src/stashbox.tests/EnumerableTests.cs b/src/stashbox.tests/EnumerableTests.cs index 3ef46f2d..891b3442 100644 --- a/src/stashbox.tests/EnumerableTests.cs +++ b/src/stashbox.tests/EnumerableTests.cs @@ -110,29 +110,28 @@ public void EnumerableTests_Resolve_Null() } [TestMethod] - public void EnumerableTests_Resolve_Scoped() + public void EnumerableTests_Resolve_Scoped_Null() { IStashboxContainer container = new StashboxContainer(); - container.RegisterType(); - container.RegisterType(); - container.RegisterType(); - var child = container.BeginScope(); + var scope = container.BeginScope(); - var all = child.Resolve>(); + var all = scope.Resolve>(); + var all2 = scope.ResolveAll(); - Assert.AreEqual(3, all.Count()); + Assert.AreEqual(0, all.Count()); + Assert.AreEqual(0, all2.Count()); } [TestMethod] - public void EnumerableTests_Resolve_Parent() + public void EnumerableTests_Resolve_Scoped() { IStashboxContainer container = new StashboxContainer(); container.RegisterType(); container.RegisterType(); container.RegisterType(); - var child = container.CreateChildContainer(); + var child = container.BeginScope(); var all = child.Resolve>(); @@ -140,17 +139,20 @@ public void EnumerableTests_Resolve_Parent() } [TestMethod] - public void EnumerableTests_Resolve_Scoped_Null() + public void EnumerableTests_Resolve_Parent() { IStashboxContainer container = new StashboxContainer(); + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); - var child = container.BeginScope(); + var child = container.CreateChildContainer(); var all = child.Resolve>(); - Assert.AreEqual(0, all.Count()); + Assert.AreEqual(3, all.Count()); } - + [TestMethod] public void EnumerableTests_Resolve_Parent_Null() { @@ -339,6 +341,30 @@ public void EnumerableTests_ResolveNonGeneric() Assert.AreEqual(2, all3.Count()); } + [TestMethod] + public void EnumerableTests_ResolveNonGeneric_Scoped() + { + IStashboxContainer container = new StashboxContainer(); + container.RegisterType(); + container.RegisterType(); + container.RegisterType(); + container.RegisterType("enumerable"); + container.RegisterType("array"); + + var scope = container.BeginScope(); + + scope.Resolve("enumerable"); + scope.Resolve("array"); + + var all = scope.Resolve>(); + var all2 = (IEnumerable)scope.ResolveAll(typeof(ITest2)); + var all3 = scope.ResolveAll(typeof(ITest2)); + + Assert.AreEqual(2, all.Count()); + Assert.AreEqual(2, all2.Count()); + Assert.AreEqual(2, all3.Count()); + } + [TestMethod] public void EnumerableTests_Parallel_Resolve() { diff --git a/src/stashbox.tests/LazyTests.cs b/src/stashbox.tests/LazyTests.cs index 8858837c..4d805dbf 100644 --- a/src/stashbox.tests/LazyTests.cs +++ b/src/stashbox.tests/LazyTests.cs @@ -115,6 +115,23 @@ public void LazyTests_Resolve_Circular() Assert.AreSame(inst1, inst2.Dep.Value); } + [TestMethod] + public void LazyTests_Resolve_Circular_FromCache() + { + var container = new StashboxContainer(config => + config.WithCircularDependencyTracking() + .WithCircularDependencyWithLazy()); + + container.RegisterSingleton(); + container.RegisterSingleton(); + + var inst1 = container.Resolve(); + var inst2 = container.Resolve(); + + Assert.AreSame(container.Resolve(), inst1.Dep.Value); + Assert.AreSame(container.Resolve(), inst2.Dep.Value); + } + [TestMethod] public void LazyTests_Resolve_Circular_Evaluate() { diff --git a/src/stashbox.tests/ResolverTests.cs b/src/stashbox.tests/ResolverTests.cs index 6bfad43c..8c8ba7ba 100644 --- a/src/stashbox.tests/ResolverTests.cs +++ b/src/stashbox.tests/ResolverTests.cs @@ -54,7 +54,7 @@ public void ResolverTests_DefaultValue_RefType_WithOptional() container.RegisterType(); var inst = container.Resolve(); - Assert.AreEqual(inst.I, null); + Assert.IsNull(inst.I); } } @@ -89,7 +89,19 @@ public void ResolverTests_DefaultValue_String() container.RegisterType(); var inst = container.Resolve(); - Assert.AreEqual(inst.I, null); + Assert.IsNull(inst.I); + } + } + + [TestMethod] + public void ResolverTests_DefaultValue_Null() + { + using (var container = new StashboxContainer()) + { + container.RegisterType(); + var inst = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(inst); } } diff --git a/src/stashbox.tests/StandardResolveTests.cs b/src/stashbox.tests/StandardResolveTests.cs index 644cf5c5..91e1701b 100644 --- a/src/stashbox.tests/StandardResolveTests.cs +++ b/src/stashbox.tests/StandardResolveTests.cs @@ -42,12 +42,49 @@ public void StandardResolveTests_Factory() { container.RegisterType(); var test1 = container.ResolveFactory(typeof(ITest1)).DynamicInvoke(); - + Assert.IsNotNull(test1); Assert.IsInstanceOfType(test1, typeof(Test1)); } } + [TestMethod] + public void StandardResolveTests_Factory_Scoped() + { + using (IStashboxContainer container = new StashboxContainer()) + { + container.RegisterType(); + using (var child = container.BeginScope()) + { + var test1 = child.ResolveFactory(typeof(ITest1)).DynamicInvoke(); + + Assert.IsNotNull(test1); + Assert.IsInstanceOfType(test1, typeof(Test1)); + } + } + } + + [TestMethod] + [ExpectedException(typeof(ResolutionFailedException))] + public void StandardResolveTests_Factory_ResolutionFailed() + { + using (IStashboxContainer container = new StashboxContainer()) + { + container.ResolveFactory(typeof(ITest1)).DynamicInvoke(); + } + } + + [TestMethod] + public void StandardResolveTests_Factory_ResolutionFailed_Null() + { + using (IStashboxContainer container = new StashboxContainer()) + { + var factory = container.ResolveFactory(typeof(ITest1), nullResultAllowed: true); + + Assert.IsNull(factory); + } + } + [TestMethod] [ExpectedException(typeof(ResolutionFailedException))] public void StandardResolveTests_DependencyResolve_ResolutionFailed() @@ -227,7 +264,7 @@ public void StandardResolveTests_Resolve_LastService() [TestMethod] public void StandardResolveTests_Resolve_MostParametersConstructor_WithoutDefault() { - using (IStashboxContainer container = new StashboxContainer(config => + using (IStashboxContainer container = new StashboxContainer(config => config.WithConstructorSelectionRule(Rules.ConstructorSelection.PreferMostParameters))) { container.RegisterType(typeof(ITest1), typeof(Test1)); @@ -265,6 +302,30 @@ public void StandardResolveTests_Resolve_LessParametersConstructor() } } + [TestMethod] + public void StandardResolveTests_Resolve_Scoped_NullDependency() + { + using (IStashboxContainer container = new StashboxContainer()) + { + container.RegisterScoped(); + var inst = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(inst); + } + } + + [TestMethod] + public void StandardResolveTests_Resolve_Singleton_NullDependency() + { + using (IStashboxContainer container = new StashboxContainer()) + { + container.RegisterSingleton(); + var inst = container.Resolve(nullResultAllowed: true); + + Assert.IsNull(inst); + } + } + public interface ITest1 { string Name { get; set; } } public interface ITest2 { string Name { get; set; } } @@ -374,5 +435,12 @@ public Test4(ITest1 test) this.Test = test; } } + + public class Test5 + { + public Test5(ITest1 test) + { + } + } } } \ No newline at end of file diff --git a/src/stashbox.tests/TupleTests.cs b/src/stashbox.tests/TupleTests.cs index 6c6da373..c7f791fd 100644 --- a/src/stashbox.tests/TupleTests.cs +++ b/src/stashbox.tests/TupleTests.cs @@ -24,6 +24,16 @@ public void TupleTests_Resolve() Assert.IsInstanceOfType(inst.Item2, typeof(Test1)); } + [TestMethod] + public void TupleTests_Resolve_Null() + { + var container = new StashboxContainer(); + container.RegisterType(); + var inst = container.Resolve>(nullResultAllowed: true); + + Assert.IsNull(inst); + } + [TestMethod] public void TupleTests_Resolve_Lazy() { diff --git a/src/stashbox/Resolution/ActivationContext.cs b/src/stashbox/Resolution/ActivationContext.cs index c18630e6..ea34497c 100644 --- a/src/stashbox/Resolution/ActivationContext.cs +++ b/src/stashbox/Resolution/ActivationContext.cs @@ -73,7 +73,10 @@ private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, IReso registration.GetExpression(resolutionInfo, type); if (initExpression == null) - throw new ResolutionFailedException(typeInfo.Type.FullName); + if (resolutionInfo.NullResultAllowed) + return null; + else + throw new ResolutionFailedException(type.FullName); var factory = Expression.Lambda(initExpression, resolutionInfo.ParameterExpressions).CompileDelegate(Constants.ScopeExpression); this.containerContext.DelegateRepository.AddFactoryDelegate(type, parameterTypes, factory, name); From 4c6eeca95ccf1dce763178b2ce49ab4e4c8d8395 Mon Sep 17 00:00:00 2001 From: Peter Csajtai Date: Fri, 24 Mar 2017 14:56:04 +0100 Subject: [PATCH 27/27] Few changes --- src/stashbox.tests/DisposeTests.cs | 2 +- src/stashbox/Infrastructure/IResolutionScope.cs | 2 +- src/stashbox/Lifetime/ScopedLifetime.cs | 2 +- src/stashbox/ResolutionScopeBase.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/stashbox.tests/DisposeTests.cs b/src/stashbox.tests/DisposeTests.cs index 8a0138e0..9c884900 100644 --- a/src/stashbox.tests/DisposeTests.cs +++ b/src/stashbox.tests/DisposeTests.cs @@ -280,7 +280,7 @@ public void DisposeTests_TrackTransientDisposal_Implementation_Has_Disposable() using (var child = container.BeginScope()) { - test1 = child.Resolve(); + test1 = (ITest11)child.Resolve(typeof(ITest11)); } Assert.IsTrue(((Test4)test1).Disposed); diff --git a/src/stashbox/Infrastructure/IResolutionScope.cs b/src/stashbox/Infrastructure/IResolutionScope.cs index 798b697b..1ccb9e8b 100644 --- a/src/stashbox/Infrastructure/IResolutionScope.cs +++ b/src/stashbox/Infrastructure/IResolutionScope.cs @@ -12,7 +12,7 @@ public interface IResolutionScope : IDisposable /// /// The key. /// The value. - void AddOrUpdateScopedItem(object key, object value); + void AddScopedItem(object key, object value); /// /// Gets an item from the scope. diff --git a/src/stashbox/Lifetime/ScopedLifetime.cs b/src/stashbox/Lifetime/ScopedLifetime.cs index b526a488..8d44b240 100644 --- a/src/stashbox/Lifetime/ScopedLifetime.cs +++ b/src/stashbox/Lifetime/ScopedLifetime.cs @@ -55,7 +55,7 @@ private static TValue GetScopedValue(IResolutionScope scope, Func(TDisposable disposable) } /// - public void AddOrUpdateScopedItem(object key, object value) => - this.scopedItems.AddOrUpdate(key, value, (oldValue, newValue) => newValue); + public void AddScopedItem(object key, object value) => + this.scopedItems.AddOrUpdate(key, value); /// public object GetScopedItemOrDefault(object key) =>