From 55e5eff24fac492181b5e66856e6723691c385f5 Mon Sep 17 00:00:00 2001 From: Eugene Sadovoi Date: Fri, 7 Jun 2019 19:57:09 -0400 Subject: [PATCH] Refactoring --- src/Context/Builder/BuilderContext.cs | 54 +- src/Factories/EnumerableResolver.cs | 6 +- src/Factories/LazyResolver.cs | 2 +- src/Lifetime/ContainerLifetimeManager.cs | 23 + .../InternalPerResolveLifetimeManager.cs | 4 +- .../Constructor/ConstructorDiagnostic.cs | 2 +- .../Constructor/ConstructorExpression.cs | 2 +- .../Constructor/ConstructorResolution.cs | 2 +- src/Pipeline/Factory/FactoryPipeline.cs | 49 +- src/Pipeline/Lifetime/LifetimePipeline.cs | 38 +- src/Registration/ExplicitRegistration.cs | 3 +- src/Registration/FactoryRegistration.cs | 4 +- src/Registration/InstanceRegistration.cs | 5 - src/{Utility => Storage}/HashHelpers.cs | 0 src/Unity.Container.csproj | 2 +- src/UnityContainer.Pipeline.cs | 514 ------------------ .../Context}/Context.cs | 0 .../Context}/Events.cs | 0 .../Context}/Implementation.cs | 0 .../Context}/Internal.cs | 0 .../Context}/Policies.cs | 0 .../IUnityContainer.cs} | 14 +- .../IUnityContainerAsync.cs} | 0 .../Implementation/Debugger.cs} | 0 .../Implementation/Implementation.cs} | 19 - .../Pipeline/CreateGetPipeline.cs | 174 ++++++ .../Pipeline/FromOpenGeneric.cs | 119 ++++ .../Pipeline/FromRegistration.cs | 186 +++++++ .../Pipeline/FromTypeFactory.cs | 97 ++++ .../Pipeline/FromUnregisteredType.cs | 172 ++++++ .../Public.cs} | 1 - .../Registration/Registration.cs} | 0 src/UnityContainer/Resolution/Array.cs | 15 + src/UnityContainer/Resolution/Enumerable.cs | 161 ++++++ .../Resolution/Resolution.cs} | 28 +- .../Utility/ExtensionExtensions.cs | 0 .../Utility/ModeFlagsExtensions.cs | 0 37 files changed, 1034 insertions(+), 662 deletions(-) create mode 100644 src/Lifetime/ContainerLifetimeManager.cs rename src/{Utility => Lifetime}/InternalPerResolveLifetimeManager.cs (86%) rename src/{Utility => Storage}/HashHelpers.cs (100%) delete mode 100644 src/UnityContainer.Pipeline.cs rename src/{Context/Container => UnityContainer/Context}/Context.cs (100%) rename src/{Context/Container => UnityContainer/Context}/Events.cs (100%) rename src/{Context/Container => UnityContainer/Context}/Implementation.cs (100%) rename src/{Context/Container => UnityContainer/Context}/Internal.cs (100%) rename src/{Context/Container => UnityContainer/Context}/Policies.cs (100%) rename src/{UnityContainer.IUnityContainer.cs => UnityContainer/IUnityContainer.cs} (95%) rename src/{UnityContainer.IUnityContainerAsync.cs => UnityContainer/IUnityContainerAsync.cs} (100%) rename src/{UnityContainer.Debugger.cs => UnityContainer/Implementation/Debugger.cs} (100%) rename src/{UnityContainer.Implementation.cs => UnityContainer/Implementation/Implementation.cs} (94%) create mode 100644 src/UnityContainer/Pipeline/CreateGetPipeline.cs create mode 100644 src/UnityContainer/Pipeline/FromOpenGeneric.cs create mode 100644 src/UnityContainer/Pipeline/FromRegistration.cs create mode 100644 src/UnityContainer/Pipeline/FromTypeFactory.cs create mode 100644 src/UnityContainer/Pipeline/FromUnregisteredType.cs rename src/{UnityContainer.Public.cs => UnityContainer/Public.cs} (99%) rename src/{UnityContainer.Registration.cs => UnityContainer/Registration/Registration.cs} (100%) create mode 100644 src/UnityContainer/Resolution/Array.cs create mode 100644 src/UnityContainer/Resolution/Enumerable.cs rename src/{UnityContainer.Resolution.cs => UnityContainer/Resolution/Resolution.cs} (95%) rename src/{ => UnityContainer}/Utility/ExtensionExtensions.cs (100%) rename src/{ => UnityContainer}/Utility/ModeFlagsExtensions.cs (100%) diff --git a/src/Context/Builder/BuilderContext.cs b/src/Context/Builder/BuilderContext.cs index ba8cc9e9..93c85224 100644 --- a/src/Context/Builder/BuilderContext.cs +++ b/src/Context/Builder/BuilderContext.cs @@ -80,7 +80,7 @@ namespace Unity.Builder if (LifetimeManager.NoValue != value) return value; } - return Resolve(pipeline); + return Resolve(type, name, pipeline); } #endregion @@ -90,7 +90,7 @@ namespace Unity.Builder public object? Get(Type policyInterface) { - return List?.Get(Type, Name, policyInterface) ?? + return List?.Get(Type, Name, policyInterface) ?? Registration?.Get(policyInterface); } @@ -381,7 +381,7 @@ namespace Unity.Builder if (LifetimeManager.NoValue != value) return value; } - return Resolve(pipeline); + return Resolve(type, null, pipeline); } public object? Resolve(Type type, IRegistration registration) @@ -391,8 +391,8 @@ namespace Unity.Builder unsafe { var thisContext = this; - var container = registration.LifetimeManager is ContainerControlledLifetimeManager containerControlled - ? (ContainerContext)containerControlled.Scope + var container = registration.LifetimeManager is ContainerControlledLifetimeManager containerControlled + ? (ContainerContext)containerControlled.Scope : ContainerContext; var context = new BuilderContext @@ -411,10 +411,10 @@ namespace Unity.Builder var manager = registration.LifetimeManager switch { - null => TransientLifetimeManager.Instance, - PerResolveLifetimeManager _ => (LifetimeManager?)context.Get(typeof(LifetimeManager)) ?? + null => TransientLifetimeManager.Instance, + PerResolveLifetimeManager _ => (LifetimeManager?)context.Get(typeof(LifetimeManager)) ?? TransientLifetimeManager.Instance, - _ => registration.LifetimeManager + _ => registration.LifetimeManager }; // Check if already got value @@ -441,26 +441,36 @@ namespace Unity.Builder } } - public object? Resolve(ResolveDelegate pipeline) + public object? Resolve(Type type, string? name, ResolveDelegate pipeline) { - // Setup Context - var synchronized = pipeline.Target as SynchronizedLifetimeManager; var thisContext = this; - try + unsafe { - // Execute pipeline - var value = pipeline(ref thisContext); - (pipeline.Target as LifetimeManager)?.Set(value, ContainerContext.Lifetime); + // Setup Context + var context = new BuilderContext + { + ContainerContext = pipeline.Target is ContainerControlledLifetimeManager containerControlled + ? (ContainerContext)containerControlled.Scope + : ContainerContext, + IsAsync = IsAsync, + List = List, + Type = type, + Name = name, + Overrides = Overrides, + DeclaringType = Type, +#if !NET40 + Parent = new IntPtr(Unsafe.AsPointer(ref thisContext)) +#endif + }; + + var manager = pipeline.Target as LifetimeManager; + var value = pipeline(ref context); + manager?.SetValue(value, ContainerContext.Lifetime); return value; } - catch when (null != synchronized) - { - synchronized.Recover(); - throw; - } - } - #endregion + #endregion + } } } diff --git a/src/Factories/EnumerableResolver.cs b/src/Factories/EnumerableResolver.cs index a8961e29..ef7f3176 100644 --- a/src/Factories/EnumerableResolver.cs +++ b/src/Factories/EnumerableResolver.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using System.Reflection; using Unity.Builder; @@ -12,7 +13,7 @@ namespace Unity.Factories private static readonly MethodInfo EnumerableMethod = typeof(EnumerableResolver).GetTypeInfo() - .GetDeclaredMethod(nameof(EnumerableResolver.Resolver)); + .GetDeclaredMethod(nameof(EnumerableResolver.EnumerableHandler)); private static readonly MethodInfo EnumerableFactory = typeof(EnumerableResolver).GetTypeInfo() @@ -29,6 +30,7 @@ namespace Unity.Factories var typeArgument = type.GetTypeInfo().GenericTypeArguments.First(); if (typeArgument.GetTypeInfo().IsGenericType) #else + Debug.Assert(0 < type.GenericTypeArguments.Length); var typeArgument = type.GenericTypeArguments.First(); if (typeArgument.IsGenericType) #endif @@ -50,7 +52,7 @@ namespace Unity.Factories #region Implementation - private static object Resolver(ref BuilderContext context) + private static object EnumerableHandler(ref BuilderContext context) { return ((UnityContainer)context.Container).ResolveEnumerable(context.Resolve, context.Name); diff --git a/src/Factories/LazyResolver.cs b/src/Factories/LazyResolver.cs index c118ea8f..c8df20fb 100644 --- a/src/Factories/LazyResolver.cs +++ b/src/Factories/LazyResolver.cs @@ -46,7 +46,7 @@ namespace Unity.Factories var lifetime = context.Get(typeof(LifetimeManager)); if (lifetime is PerResolveLifetimeManager) { - var perBuildLifetime = new InternalPerResolveLifetimeManager(context.Existing); + var perBuildLifetime = new RuntimePerResolveLifetimeManager(context.Existing); context.Set(typeof(LifetimeManager), perBuildLifetime); } diff --git a/src/Lifetime/ContainerLifetimeManager.cs b/src/Lifetime/ContainerLifetimeManager.cs new file mode 100644 index 00000000..d3db4489 --- /dev/null +++ b/src/Lifetime/ContainerLifetimeManager.cs @@ -0,0 +1,23 @@ +using System; +using System.Diagnostics; + +namespace Unity.Lifetime +{ + internal class ContainerLifetimeManager : LifetimeManager + { + protected override LifetimeManager OnCreateLifetimeManager() + => throw new NotImplementedException(); + + public override object GetValue(ILifetimeContainer container) + { + Debug.Assert(null != container); + return container.Container; + } + + public override object TryGetValue(ILifetimeContainer container) + { + Debug.Assert(null != container); + return container.Container; + } + } +} diff --git a/src/Utility/InternalPerResolveLifetimeManager.cs b/src/Lifetime/InternalPerResolveLifetimeManager.cs similarity index 86% rename from src/Utility/InternalPerResolveLifetimeManager.cs rename to src/Lifetime/InternalPerResolveLifetimeManager.cs index 15a2bed8..818645b7 100644 --- a/src/Utility/InternalPerResolveLifetimeManager.cs +++ b/src/Lifetime/InternalPerResolveLifetimeManager.cs @@ -7,7 +7,7 @@ namespace Unity.Lifetime /// but also provides a signal to the default build plan, marking the type so that /// instances are reused across the build up object graph. /// - internal class InternalPerResolveLifetimeManager : PerResolveLifetimeManager + internal class RuntimePerResolveLifetimeManager : PerResolveLifetimeManager { /// /// Construct a new object that stores the @@ -17,7 +17,7 @@ namespace Unity.Lifetime /// normal method is not used here. /// /// InjectionParameterValue to store. - public InternalPerResolveLifetimeManager(object? value) + public RuntimePerResolveLifetimeManager(object? value) { base.value = value; InUse = true; diff --git a/src/Pipeline/Constructor/ConstructorDiagnostic.cs b/src/Pipeline/Constructor/ConstructorDiagnostic.cs index 24cdccb9..50af1c7a 100644 --- a/src/Pipeline/Constructor/ConstructorDiagnostic.cs +++ b/src/Pipeline/Constructor/ConstructorDiagnostic.cs @@ -417,7 +417,7 @@ namespace Unity } context.Set(typeof(LifetimeManager), - new InternalPerResolveLifetimeManager(context.Existing)); + new RuntimePerResolveLifetimeManager(context.Existing)); } return null == pipeline ? context.Existing : pipeline.Invoke(ref context); diff --git a/src/Pipeline/Constructor/ConstructorExpression.cs b/src/Pipeline/Constructor/ConstructorExpression.cs index ceffcb34..4650cfb8 100644 --- a/src/Pipeline/Constructor/ConstructorExpression.cs +++ b/src/Pipeline/Constructor/ConstructorExpression.cs @@ -11,7 +11,7 @@ namespace Unity { #region Fields - private static readonly ConstructorInfo PerResolveInfo = typeof(InternalPerResolveLifetimeManager) + private static readonly ConstructorInfo PerResolveInfo = typeof(RuntimePerResolveLifetimeManager) .GetTypeInfo().DeclaredConstructors.First(); protected static readonly Expression SetPerBuildSingletonExpr = diff --git a/src/Pipeline/Constructor/ConstructorResolution.cs b/src/Pipeline/Constructor/ConstructorResolution.cs index a881adf4..160dad4e 100644 --- a/src/Pipeline/Constructor/ConstructorResolution.cs +++ b/src/Pipeline/Constructor/ConstructorResolution.cs @@ -122,7 +122,7 @@ namespace Unity context.Existing = info.Invoke(dependencies); context.Set(typeof(LifetimeManager), - new InternalPerResolveLifetimeManager(context.Existing)); + new RuntimePerResolveLifetimeManager(context.Existing)); } return null == pipeline ? context.Existing : pipeline.Invoke(ref context); diff --git a/src/Pipeline/Factory/FactoryPipeline.cs b/src/Pipeline/Factory/FactoryPipeline.cs index f9550adc..057c55cd 100644 --- a/src/Pipeline/Factory/FactoryPipeline.cs +++ b/src/Pipeline/Factory/FactoryPipeline.cs @@ -43,51 +43,26 @@ namespace Unity // Try finding factory TypeFactoryDelegate? factory = builder.Policies?.Get(); - if (builder.Policies is ExplicitRegistration @explicit) - { #if NETCOREAPP1_0 || NETSTANDARD1_0 - if (null != builder.Type && builder.Type.GetTypeInfo().IsGenericType) + if (null != builder.Type && builder.Type.GetTypeInfo().IsGenericType) #else - if (null != builder.Type && builder.Type.IsGenericType) + if (null != builder.Type && builder.Type.IsGenericType) #endif - { - factory = (TypeFactoryDelegate?)builder.ContainerContext.Get(builder.Type.GetGenericTypeDefinition(), - typeof(TypeFactoryDelegate)); - } - else if (null != builder.Type && builder.Type.IsArray) - { - if (builder.Type.GetArrayRank() == 1) - { - var resolve = ArrayResolver.Factory(builder.Type, builder.ContainerContext.Container); - return builder.Pipeline((ref BuilderContext context) => resolve(ref context)); - } - else - { - var message = $"Invalid array {builder.Type}. Only arrays of rank 1 are supported"; - return (ref BuilderContext context) => throw new InvalidRegistrationException(message); - } - } + { + factory = (TypeFactoryDelegate?)builder.ContainerContext.Get(builder.Type.GetGenericTypeDefinition(), + typeof(TypeFactoryDelegate)); } - else if(builder.Policies is ImplicitRegistration @implicit) + else if (null != builder.Type && builder.Type.IsArray) { -#if NETCOREAPP1_0 || NETSTANDARD1_0 - if (null != builder.Type && builder.Type.GetTypeInfo().IsGenericType) -#else - if (null != builder.Type && builder.Type.IsGenericType) -#endif + if (builder.Type.GetArrayRank() == 1) { - factory = (TypeFactoryDelegate?)builder.ContainerContext.Get(builder.Type.GetGenericTypeDefinition(), - typeof(TypeFactoryDelegate)); + var resolve = ArrayResolver.Factory(builder.Type, builder.ContainerContext.Container); + return builder.Pipeline((ref BuilderContext context) => resolve(ref context)); } - else if (builder.Type?.IsArray ?? false) + else { - if (builder.Type?.GetArrayRank() == 1) - return builder.Pipeline(ArrayResolver.Factory(builder.Type, builder.ContainerContext.Container)); - else - { - var message = $"Invalid array {builder.Type}. Only arrays of rank 1 are supported"; - return (ref BuilderContext context) => throw new InvalidRegistrationException(message); - } + var message = $"Invalid array {builder.Type}. Only arrays of rank 1 are supported"; + return (ref BuilderContext context) => throw new InvalidRegistrationException(message); } } diff --git a/src/Pipeline/Lifetime/LifetimePipeline.cs b/src/Pipeline/Lifetime/LifetimePipeline.cs index 92634314..ef1d2713 100644 --- a/src/Pipeline/Lifetime/LifetimePipeline.cs +++ b/src/Pipeline/Lifetime/LifetimePipeline.cs @@ -2,7 +2,6 @@ using Unity.Builder; using Unity.Lifetime; using Unity.Resolution; -using static Unity.UnityContainer; namespace Unity { @@ -12,31 +11,24 @@ namespace Unity public override ResolveDelegate? Build(ref PipelineBuilder builder) { - if (builder.LifetimeManager is ContainerControlledLifetimeManager manager) + ResolveDelegate? pipeline = builder.Pipeline(); + Debug.Assert(null != pipeline); + + return builder.LifetimeManager is SynchronizedLifetimeManager manager ? + (ref BuilderContext context) => { - var pipeline = builder.Pipeline(); - Debug.Assert(null != pipeline); - - return (ref BuilderContext context) => + try { - var scope = context.ContainerContext; - - try - { - // Switch context to lifetime's scope - context.ContainerContext = (ContainerContext)manager.Scope; - - // Build withing the scope - return pipeline(ref context); - } - finally - { - context.ContainerContext = scope; - } - }; + // Build withing the scope + return pipeline(ref context); + } + catch + { + manager.Recover(); + throw; + } } - - return builder.Pipeline(); ; + : pipeline; } #endregion diff --git a/src/Registration/ExplicitRegistration.cs b/src/Registration/ExplicitRegistration.cs index 1b9479af..f9769520 100644 --- a/src/Registration/ExplicitRegistration.cs +++ b/src/Registration/ExplicitRegistration.cs @@ -19,6 +19,7 @@ namespace Unity.Registration { Next = null; Type = type; + LifetimeManager = new TransientLifetimeManager(); InjectionMembers = null; BuildRequired = false; } @@ -40,7 +41,7 @@ namespace Unity.Registration : base(owner, name) { Type = type; - LifetimeManager = lifetimeManager is TransientLifetimeManager ? null : lifetimeManager; + LifetimeManager = lifetimeManager; Next = owner.Defaults; InjectionMembers = null != injectionMembers && 0 < injectionMembers.Length ? injectionMembers : null; BuildRequired = null != InjectionMembers && InjectionMembers.Any(m => m.BuildRequired) || lifetimeManager is PerResolveLifetimeManager; diff --git a/src/Registration/FactoryRegistration.cs b/src/Registration/FactoryRegistration.cs index 0b6b668d..7d5e04dd 100644 --- a/src/Registration/FactoryRegistration.cs +++ b/src/Registration/FactoryRegistration.cs @@ -15,7 +15,7 @@ namespace Unity.Registration if (manager is IDisposable managerDisposable) owner.Context.Lifetime.Add(managerDisposable); - LifetimeManager = manager is TransientLifetimeManager ? null : manager; + LifetimeManager = manager; // Factory resolver if (manager is PerResolveLifetimeManager) @@ -23,7 +23,7 @@ namespace Unity.Registration Pipeline = (ref BuilderContext context) => { var value = factory(context.Container, context.Type, context.Name); - context.Set(typeof(LifetimeManager), new InternalPerResolveLifetimeManager(value)); + context.Set(typeof(LifetimeManager), new RuntimePerResolveLifetimeManager(value)); return value; }; } diff --git a/src/Registration/InstanceRegistration.cs b/src/Registration/InstanceRegistration.cs index 30fb8939..93c40080 100644 --- a/src/Registration/InstanceRegistration.cs +++ b/src/Registration/InstanceRegistration.cs @@ -19,11 +19,6 @@ namespace Unity.Registration manager.InUse = true; manager.SetValue(instance, owner.Context.Lifetime); Pipeline = (ref BuilderContext context) => throw new InvalidOperationException("Instance value no longer available"); - //Pipeline = manager switch - //{ - // ExternallyControlledLifetimeManager _ => ExternalLifetime, - // _ => (ResolveDelegate)OtherLifetime - //}; LifetimeManager = manager; } diff --git a/src/Utility/HashHelpers.cs b/src/Storage/HashHelpers.cs similarity index 100% rename from src/Utility/HashHelpers.cs rename to src/Storage/HashHelpers.cs diff --git a/src/Unity.Container.csproj b/src/Unity.Container.csproj index 043347b5..9295cd8a 100644 --- a/src/Unity.Container.csproj +++ b/src/Unity.Container.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/UnityContainer.Pipeline.cs b/src/UnityContainer.Pipeline.cs deleted file mode 100644 index f881c005..00000000 --- a/src/UnityContainer.Pipeline.cs +++ /dev/null @@ -1,514 +0,0 @@ -using System; -using System.Diagnostics; -using System.Reflection; -using System.Threading; -using Unity.Builder; -using Unity.Lifetime; -using Unity.Registration; -using Unity.Resolution; -using Unity.Storage; - -namespace Unity -{ - public partial class UnityContainer - { - #region Get Pipeline - - private ResolveDelegate? TryGetPipeline(ref HashKey key) - { - // Iterate through containers hierarchy - for (UnityContainer? container = this; null != container; container = container._parent) - { - // Skip to parent if no registrations - if (null == container._metadata) continue; - - Debug.Assert(null != container._registry); - var registry = container._registry; - - // Check for exact match - for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next) - { - ref var candidate = ref registry.Entries[i]; - if (candidate.Key != key) continue; - - // Found it - return candidate.Pipeline; - } - } - - return null; - } - - internal ResolveDelegate GetPipeline(ref HashKey key) - { -#if NETSTANDARD1_0 || NETCOREAPP1_0 - var info = key.Type?.GetTypeInfo(); - return null != info && info.IsGenericType - ? GenericGetPipeline(ref key, info) - : GetNonGenericPipeline(ref key); -#else - return null != key.Type && key.Type.IsGenericType - ? GenericGetPipeline(ref key) - : GetNonGenericPipeline(ref key); -#endif - } - - #endregion - - - #region Implementation - - private ResolveDelegate GetNonGenericPipeline(ref HashKey key) - { - // Iterate through containers hierarchy - for (UnityContainer? container = this; null != container; container = container._parent) - { - // Skip to parent if no registrations - if (null == container._metadata) continue; - - Debug.Assert(null != container._registry); - var registry = container._registry; - - // Check for exact match - for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next) - { - ref var candidate = ref registry.Entries[i]; - if (candidate.Key != key) continue; - - // Found it - Debug.Assert(null != candidate.Pipeline); - return candidate.Pipeline; - } - } - - Debug.Assert(null != _root); - - return _root.PipelineFromType(ref key); - } - -#if NETSTANDARD1_0 || NETCOREAPP1_0 - private ResolveDelegate GenericGetPipeline(ref HashKey key, TypeInfo info) - { - Debug.Assert(null != info); -#else - private ResolveDelegate GenericGetPipeline(ref HashKey key) - { -#endif - Debug.Assert(null != key.Type); - - int targetBucket; - bool initGenerics = true; - var keyGeneric = new HashKey(); - var keyDefault = new HashKey(); - Type type = key.Type; - Type? generic = null; - var name = key.Name; - - // Iterate through containers hierarchy - for (UnityContainer? container = this; null != container; container = container._parent) - { - // Skip to parent if no registrations - if (null == container._metadata) continue; - - Debug.Assert(null != container._registry); - var registry = container._registry; - - // Exact match - targetBucket = key.HashCode % registry.Buckets.Length; - for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next) - { - ref var candidate = ref registry.Entries[i]; - if (candidate.Key != key) continue; - - Debug.Assert(null != candidate.Pipeline); - - // Found a registration - return candidate.Pipeline; - } - - // Generic registrations - if (initGenerics) - { - initGenerics = false; - -#if NETSTANDARD1_0 || NETCOREAPP1_0 - generic = info.GetGenericTypeDefinition(); -#else - generic = type.GetGenericTypeDefinition(); -#endif - keyGeneric = new HashKey(generic, name); - keyDefault = new HashKey(generic); - } - - // Factory with the same name - targetBucket = keyGeneric.HashCode % registry.Buckets.Length; - for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next) - { - ref var candidate = ref registry.Entries[i]; - if (candidate.Key != keyGeneric) - continue; - - // Found a factory - return container.PipelineFromFactory(ref key, (ExplicitRegistration)candidate.Policies); - } - - // Default factory - targetBucket = keyDefault.HashCode % registry.Buckets.Length; - for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next) - { - ref var candidate = ref registry.Entries[i]; - if (candidate.Key != keyDefault) - continue; - - // Found a factory - var typeFactory = (TypeFactoryDelegate)candidate.Policies.Get(typeof(TypeFactoryDelegate)); - return container.PipelineFromTypeFactory(ref key, container, typeFactory); - } - } - - Debug.Assert(null != _root); - - return _root.PipelineFromType(ref key); - } - - #endregion - - - #region Create Pipeline - - private ResolveDelegate PipelineFromType(ref HashKey key) - { - Debug.Assert(null != key.Type); - Debug.Assert(null != _registry); - - var type = key.Type; - var name = key.Name; - int count = -1; - var collisions = 0; - ResolveDelegate? pipeline = null; - - lock (_syncLock) - { - var targetBucket = key.HashCode % _registry.Buckets.Length; - for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next) - { - ref var candidate = ref _registry.Entries[i]; - if (candidate.Key != key) - { - collisions++; - continue; - } - - if (null == candidate.Pipeline) - candidate.Pipeline = buildPipeline; - - // Replaced registration - return candidate.Pipeline; - } - - // Expand if required - if (_registry.RequireToGrow || CollisionsCutPoint < collisions) - { - _registry = new Registry(_registry); - targetBucket = key.HashCode % _registry.Buckets.Length; - } - - // Create new entry - ref var entry = ref _registry.Entries[_registry.Count]; - entry.Key = key; - entry.Next = _registry.Buckets[targetBucket]; - entry.Type = type; - entry.IsExplicit = true; - entry.Pipeline = buildPipeline; - int position = _registry.Count++; - _registry.Buckets[targetBucket] = position; - } - - return buildPipeline; - - - object? buildPipeline(ref BuilderContext context) - { - // Wait for right moment - while (0 != Interlocked.Increment(ref count)) - { - Interlocked.Decrement(ref count); -#if NETSTANDARD1_0 || NETCOREAPP1_0 - for (var i = 0; i < 100; i++) ; -#else - Thread.SpinWait(100); -#endif - } - - try - { - // Create if required - if (null == pipeline) - { - PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache); - pipeline = builder.Pipeline(); - - Debug.Assert(null != pipeline); - } - - return pipeline(ref context); - } - finally - { - Interlocked.Decrement(ref count); - } - }; - - } - - private ResolveDelegate PipelineFromRegistration(ref HashKey key, ExplicitRegistration registration, int position) - { - Debug.Assert(null != _registry); - - var type = key.Type; - var name = key.Name; - ResolveDelegate? pipeline = null; - - if (null != registration.LifetimeManager && !(registration.LifetimeManager is TransientLifetimeManager)) - { - registration.LifetimeManager.PipelineDelegate = (ResolveDelegate)BuildPipeline; - return registration.LifetimeManager.Pipeline; - } - - return BuildPipeline; - - object? BuildPipeline(ref BuilderContext context) - { - if (null != pipeline) return pipeline(ref context); - lock (registration) - { - if (null != pipeline) return pipeline(ref context); - - PipelineBuilder builder = new PipelineBuilder(type, registration); - - if (registration.LifetimeManager is LifetimeManager manager) - { - manager.PipelineDelegate = builder.Pipeline(); - pipeline = manager.Pipeline; - } - else - pipeline = builder.Pipeline(); - - Debug.Assert(null != pipeline); - Debug.Assert(null != _registry); - - lock (_syncLock) - { - if (ReferenceEquals(registration, _registry.Entries[position].Policies)) - { - _registry.Entries[position].Pipeline = pipeline; - } - } - - // Check if already created and acquire a lock if not - if (pipeline.Target is LifetimeManager lifetime) - { - // Make blocking check for result - var value = lifetime.Get(LifetimeContainer); - if (LifetimeManager.NoValue != value) return value; - } - - return pipeline(ref context); - } - }; - } - - private ResolveDelegate PipelineFromFactory(ref HashKey key, ExplicitRegistration factory) - { - Debug.Assert(null != _registry); - Debug.Assert(null != key.Type); - - var type = key.Type; - var name = key.Name; - var owner = this; - - int count = -1; - int position = 0; - var collisions = 0; - ResolveDelegate? pipeline = null; - ResolveDelegate buildPipeline; - var manager = null != factory.LifetimeManager && !(factory.LifetimeManager is TransientLifetimeManager) - ? factory.LifetimeManager.CreateLifetimePolicy() - : null; - - if (null != manager) - { - if (manager is IDisposable disposable) Context.Lifetime.Add(disposable); - if (manager is ContainerControlledLifetimeManager containerControlled) containerControlled.Scope = Context; - - manager.PipelineDelegate = (ResolveDelegate)BuildPipeline; - buildPipeline = manager.Pipeline; - } - else - buildPipeline = BuildPipeline; - - // Add Pipeline to the Registry - lock (_syncLock) - { - var targetBucket = key.HashCode % _registry.Buckets.Length; - for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next) - { - ref var candidate = ref _registry.Entries[i]; - if (candidate.Key != key) - { - collisions++; - continue; - } - - Debug.Assert(null != candidate.Pipeline); - - // Has already been created - return candidate.Pipeline; - } - - // Expand if required - if (_registry.RequireToGrow || CollisionsCutPoint < collisions) - { - _registry = new Registry(_registry); - targetBucket = key.HashCode % _registry.Buckets.Length; - } - - // Create new entry - ref var entry = ref _registry.Entries[_registry.Count]; - entry.Key = key; - entry.Type = type; - entry.Pipeline = buildPipeline; - entry.Next = _registry.Buckets[targetBucket]; - position = _registry.Count++; - _registry.Buckets[targetBucket] = position; - } - - // Return temporary pipeline - return buildPipeline; - - - // Create pipeline and add to Registry - object? BuildPipeline(ref BuilderContext context) - { - // Wait for right moment - while (0 != Interlocked.Increment(ref count)) - { - Interlocked.Decrement(ref count); -#if NETSTANDARD1_0 || NETCOREAPP1_0 - for (var i = 0; i < 100; i++) ; -#else - Thread.SpinWait(100); -#endif - } - - try - { - // Create if required - if (null == pipeline) - { - PipelineBuilder builder = new PipelineBuilder(type, factory, manager, owner); - pipeline = builder.Pipeline(); - if (null != manager) manager.PipelineDelegate = pipeline; - Debug.Assert(null != pipeline); - } - - var value = pipeline(ref context); - manager?.Set(value, LifetimeContainer); - return value; - } - finally - { - Interlocked.Decrement(ref count); - } - }; - } - - private ResolveDelegate PipelineFromTypeFactory(ref HashKey key, UnityContainer container, TypeFactoryDelegate typeFactory) - { - Debug.Assert(null != _registry); - Debug.Assert(null != key.Type); - - var type = key.Type; - var name = key.Name; - var owner = this; - - int count = -1; - int position = 0; - var collisions = 0; - ResolveDelegate? pipeline = null; - - // Add Pipeline to the Registry - lock (_syncLock) - { - var targetBucket = key.HashCode % _registry.Buckets.Length; - for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next) - { - ref var candidate = ref _registry.Entries[i]; - if (candidate.Key != key) - { - collisions++; - continue; - } - - Debug.Assert(null != candidate.Pipeline); - - // Has already been created - return candidate.Pipeline; - } - - // Expand if required - if (_registry.RequireToGrow || CollisionsCutPoint < collisions) - { - _registry = new Registry(_registry); - targetBucket = key.HashCode % _registry.Buckets.Length; - } - - // Create new entry - ref var entry = ref _registry.Entries[_registry.Count]; - entry.Key = key; - entry.Type = type; - entry.Pipeline = BuildPipeline; - entry.Next = _registry.Buckets[targetBucket]; - position = _registry.Count++; - _registry.Buckets[targetBucket] = position; - } - - // Return temporary pipeline - return BuildPipeline; - - - // Create pipeline and add to Registry - object? BuildPipeline(ref BuilderContext context) - { - // Wait for right moment - while (0 != Interlocked.Increment(ref count)) - { - Interlocked.Decrement(ref count); -#if NETSTANDARD1_0 || NETCOREAPP1_0 - for (var i = 0; i < 100; i++) ; -#else - Thread.SpinWait(100); -#endif - } - - try - { - // Create if required - if (null == pipeline) - { - pipeline = typeFactory(type, container); - Debug.Assert(null != pipeline); - } - - return pipeline(ref context); - } - finally - { - Interlocked.Decrement(ref count); - } - }; - } - - #endregion - } -} diff --git a/src/Context/Container/Context.cs b/src/UnityContainer/Context/Context.cs similarity index 100% rename from src/Context/Container/Context.cs rename to src/UnityContainer/Context/Context.cs diff --git a/src/Context/Container/Events.cs b/src/UnityContainer/Context/Events.cs similarity index 100% rename from src/Context/Container/Events.cs rename to src/UnityContainer/Context/Events.cs diff --git a/src/Context/Container/Implementation.cs b/src/UnityContainer/Context/Implementation.cs similarity index 100% rename from src/Context/Container/Implementation.cs rename to src/UnityContainer/Context/Implementation.cs diff --git a/src/Context/Container/Internal.cs b/src/UnityContainer/Context/Internal.cs similarity index 100% rename from src/Context/Container/Internal.cs rename to src/UnityContainer/Context/Internal.cs diff --git a/src/Context/Container/Policies.cs b/src/UnityContainer/Context/Policies.cs similarity index 100% rename from src/Context/Container/Policies.cs rename to src/UnityContainer/Context/Policies.cs diff --git a/src/UnityContainer.IUnityContainer.cs b/src/UnityContainer/IUnityContainer.cs similarity index 95% rename from src/UnityContainer.IUnityContainer.cs rename to src/UnityContainer/IUnityContainer.cs index 0620173c..3e04185b 100644 --- a/src/UnityContainer.IUnityContainer.cs +++ b/src/UnityContainer/IUnityContainer.cs @@ -219,7 +219,6 @@ namespace Unity } // Setup Context - var synchronized = manager as SynchronizedLifetimeManager; var context = new BuilderContext { List = new PolicyList(), @@ -237,17 +236,10 @@ namespace Unity return value; } catch (Exception ex) + when (ex is InvalidRegistrationException || ex is CircularDependencyException || ex is ObjectDisposedException) { - synchronized?.Recover(); - - if (ex is InvalidRegistrationException || - ex is CircularDependencyException || - ex is ObjectDisposedException) - { - var message = CreateErrorMessage(ex); - throw new ResolutionFailedException(context.Type, context.Name, message, ex); - } - else throw; + var message = CreateErrorMessage(ex); + throw new ResolutionFailedException(context.Type, context.Name, message, ex); } } diff --git a/src/UnityContainer.IUnityContainerAsync.cs b/src/UnityContainer/IUnityContainerAsync.cs similarity index 100% rename from src/UnityContainer.IUnityContainerAsync.cs rename to src/UnityContainer/IUnityContainerAsync.cs diff --git a/src/UnityContainer.Debugger.cs b/src/UnityContainer/Implementation/Debugger.cs similarity index 100% rename from src/UnityContainer.Debugger.cs rename to src/UnityContainer/Implementation/Debugger.cs diff --git a/src/UnityContainer.Implementation.cs b/src/UnityContainer/Implementation/Implementation.cs similarity index 94% rename from src/UnityContainer.Implementation.cs rename to src/UnityContainer/Implementation/Implementation.cs index ce777830..1fbae5fb 100644 --- a/src/UnityContainer.Implementation.cs +++ b/src/UnityContainer/Implementation/Implementation.cs @@ -284,24 +284,5 @@ namespace Unity } #endregion - - - private class ContainerLifetimeManager : LifetimeManager - { - protected override LifetimeManager OnCreateLifetimeManager() - => throw new NotImplementedException(); - - public override object GetValue(ILifetimeContainer container) - { - Debug.Assert(null != container); - return container.Container; - } - - public override object TryGetValue(ILifetimeContainer container) - { - Debug.Assert(null != container); - return container.Container; - } - } } } diff --git a/src/UnityContainer/Pipeline/CreateGetPipeline.cs b/src/UnityContainer/Pipeline/CreateGetPipeline.cs new file mode 100644 index 00000000..2511c482 --- /dev/null +++ b/src/UnityContainer/Pipeline/CreateGetPipeline.cs @@ -0,0 +1,174 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using Unity.Builder; +using Unity.Registration; +using Unity.Resolution; +using Unity.Storage; + +namespace Unity +{ + public partial class UnityContainer + { + #region Get Pipeline + + private ResolveDelegate? TryGetPipeline(ref HashKey key) + { + // Iterate through containers hierarchy + for (UnityContainer? container = this; null != container; container = container._parent) + { + // Skip to parent if no registrations + if (null == container._metadata) continue; + + Debug.Assert(null != container._registry); + var registry = container._registry; + + // Check for exact match + for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next) + { + ref var candidate = ref registry.Entries[i]; + if (candidate.Key != key) continue; + + // Found it + return candidate.Pipeline; + } + } + + return null; + } + + internal ResolveDelegate GetPipeline(ref HashKey key) + { +#if NETSTANDARD1_0 || NETCOREAPP1_0 + var info = key.Type?.GetTypeInfo(); + return null != info && info.IsGenericType + ? GenericGetPipeline(ref key, info) + : GetNonGenericPipeline(ref key); +#else + return null != key.Type && key.Type.IsGenericType + ? GenericGetPipeline(ref key) + : GetNonGenericPipeline(ref key); +#endif + } + + #endregion + + + #region Implementation + + private ResolveDelegate GetNonGenericPipeline(ref HashKey key) + { + // Iterate through containers hierarchy + for (UnityContainer? container = this; null != container; container = container._parent) + { + // Skip to parent if no registrations + if (null == container._metadata) continue; + + Debug.Assert(null != container._registry); + var registry = container._registry; + + // Check for exact match + for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next) + { + ref var candidate = ref registry.Entries[i]; + if (candidate.Key != key) continue; + + // Found it + Debug.Assert(null != candidate.Pipeline); + return candidate.Pipeline; + } + } + + Debug.Assert(null != _root); + + return _root.PipelineFromUnregisteredType(ref key); + } + +#if NETSTANDARD1_0 || NETCOREAPP1_0 + private ResolveDelegate GenericGetPipeline(ref HashKey key, TypeInfo info) + { + Debug.Assert(null != info); +#else + private ResolveDelegate GenericGetPipeline(ref HashKey key) + { +#endif + Debug.Assert(null != key.Type); + + int targetBucket; + bool initGenerics = true; + var keyGeneric = new HashKey(); + var keyDefault = new HashKey(); + Type type = key.Type; + Type? generic = null; + var name = key.Name; + + // Iterate through containers hierarchy + for (UnityContainer? container = this; null != container; container = container._parent) + { + // Skip to parent if no registrations + if (null == container._metadata) continue; + + Debug.Assert(null != container._registry); + var registry = container._registry; + + // Exact match + targetBucket = key.HashCode % registry.Buckets.Length; + for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next) + { + ref var candidate = ref registry.Entries[i]; + if (candidate.Key != key) continue; + + Debug.Assert(null != candidate.Pipeline); + + // Found a registration + return candidate.Pipeline; + } + + // Generic registrations + if (initGenerics) + { + initGenerics = false; + +#if NETSTANDARD1_0 || NETCOREAPP1_0 + generic = info.GetGenericTypeDefinition(); +#else + generic = type.GetGenericTypeDefinition(); +#endif + keyGeneric = new HashKey(generic, name); + keyDefault = new HashKey(generic); + } + + // Factory with the same name + targetBucket = keyGeneric.HashCode % registry.Buckets.Length; + for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next) + { + ref var candidate = ref registry.Entries[i]; + if (candidate.Key != keyGeneric) + continue; + + // Found a factory + return container.PipelineFromOpenGeneric(ref key, (ExplicitRegistration)candidate.Policies); + } + + // Default factory + targetBucket = keyDefault.HashCode % registry.Buckets.Length; + for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next) + { + ref var candidate = ref registry.Entries[i]; + if (candidate.Key != keyDefault) + continue; + + // Found a factory + var typeFactory = (TypeFactoryDelegate)candidate.Policies.Get(typeof(TypeFactoryDelegate)); + return container.PipelineFromTypeFactory(ref key, container, typeFactory); + } + } + + Debug.Assert(null != _root); + + return _root.PipelineFromUnregisteredType(ref key); + } + + #endregion + } +} diff --git a/src/UnityContainer/Pipeline/FromOpenGeneric.cs b/src/UnityContainer/Pipeline/FromOpenGeneric.cs new file mode 100644 index 00000000..1b4100c9 --- /dev/null +++ b/src/UnityContainer/Pipeline/FromOpenGeneric.cs @@ -0,0 +1,119 @@ +using System; +using System.Diagnostics; +using System.Threading; +using Unity.Builder; +using Unity.Lifetime; +using Unity.Registration; +using Unity.Resolution; +using Unity.Storage; + +namespace Unity +{ + public partial class UnityContainer + { + private ResolveDelegate PipelineFromOpenGeneric(ref HashKey key, ExplicitRegistration factory) + { + Debug.Assert(null != _registry); + Debug.Assert(null != key.Type); + + var type = key.Type; + var name = key.Name; + var owner = this; + + int count = -1; + int position = 0; + var collisions = 0; + ResolveDelegate? pipeline = null; + ResolveDelegate buildPipeline; + var manager = null != factory.LifetimeManager && !(factory.LifetimeManager is TransientLifetimeManager) + ? factory.LifetimeManager.CreateLifetimePolicy() + : null; + + if (null != manager) + { + if (manager is IDisposable disposable) Context.Lifetime.Add(disposable); + if (manager is ContainerControlledLifetimeManager containerControlled) containerControlled.Scope = Context; + + manager.PipelineDelegate = (ResolveDelegate)BuildPipeline; + buildPipeline = manager.Pipeline; + } + else + buildPipeline = BuildPipeline; + + // Add Pipeline to the Registry + lock (_syncLock) + { + var targetBucket = key.HashCode % _registry.Buckets.Length; + for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next) + { + ref var candidate = ref _registry.Entries[i]; + if (candidate.Key != key) + { + collisions++; + continue; + } + + Debug.Assert(null != candidate.Pipeline); + + // Has already been created + return candidate.Pipeline; + } + + // Expand if required + if (_registry.RequireToGrow || CollisionsCutPoint < collisions) + { + _registry = new Registry(_registry); + targetBucket = key.HashCode % _registry.Buckets.Length; + } + + // Create new entry + ref var entry = ref _registry.Entries[_registry.Count]; + entry.Key = key; + entry.Type = type; + entry.Pipeline = buildPipeline; + entry.Next = _registry.Buckets[targetBucket]; + position = _registry.Count++; + _registry.Buckets[targetBucket] = position; + } + + // Return temporary pipeline + return buildPipeline; + + + // Create pipeline and add to Registry + object? BuildPipeline(ref BuilderContext context) + { + // Wait for right moment + while (0 != Interlocked.Increment(ref count)) + { + Interlocked.Decrement(ref count); +#if NETSTANDARD1_0 || NETCOREAPP1_0 + for (var i = 0; i < 100; i++) ; +#else + Thread.SpinWait(100); +#endif + } + + try + { + // Create if required + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, factory, manager, owner); + pipeline = builder.Pipeline(); + if (null != manager) manager.PipelineDelegate = pipeline; + Debug.Assert(null != pipeline); + } + + var value = pipeline(ref context); + manager?.Set(value, LifetimeContainer); + return value; + } + finally + { + Interlocked.Decrement(ref count); + } + }; + } + } +} diff --git a/src/UnityContainer/Pipeline/FromRegistration.cs b/src/UnityContainer/Pipeline/FromRegistration.cs new file mode 100644 index 00000000..b19ef1b4 --- /dev/null +++ b/src/UnityContainer/Pipeline/FromRegistration.cs @@ -0,0 +1,186 @@ +using System; +using System.Diagnostics; +using Unity.Builder; +using Unity.Lifetime; +using Unity.Registration; +using Unity.Resolution; +using Unity.Storage; + +namespace Unity +{ + public partial class UnityContainer + { + private ResolveDelegate PipelineFromRegistration(ref HashKey key, ExplicitRegistration registration, int position) + { + Debug.Assert(null != _registry); + Debug.Assert(null != key.Type); + Debug.Assert(null != registration.LifetimeManager); + + registration.LifetimeManager.PipelineDelegate = registration.LifetimeManager switch + { + TransientLifetimeManager _ => PipelineFromRegistrationTransient(key.Type, registration, position), + SynchronizedLifetimeManager _ => PipelineFromRegistrationSynchronized(key.Type, registration), + PerResolveLifetimeManager _ => PipelineFromRegistrationPerResolve(key.Type, registration), + _ => PipelineFromRegistrationDefault(key.Type, registration) + }; + + return registration.LifetimeManager.Pipeline; + } + + private ResolveDelegate PipelineFromRegistrationTransient(Type type, ExplicitRegistration registration, int position) + { + ResolveDelegate? pipeline = null; + + return (ref BuilderContext context) => + { + if (null != pipeline) return pipeline(ref context); + + lock (registration) + { + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, registration); + + pipeline = builder.Pipeline(); + + Debug.Assert(null != pipeline); + Debug.Assert(null != _registry); + + lock (_syncLock) + { + _registry.Entries[position].Pipeline = pipeline; + } + } + + return pipeline(ref context); + } + }; + } + + private ResolveDelegate PipelineFromRegistrationSynchronized(Type type, ExplicitRegistration registration) + { + ResolveDelegate? pipeline = null; + var manager = registration.LifetimeManager as SynchronizedLifetimeManager; + Debug.Assert(null != manager); + + return (ref BuilderContext context) => + { + object value; + + if (null != pipeline) + { + value = manager.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + return pipeline(ref context); + } + + lock (registration) + { + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, registration); + + pipeline = builder.Pipeline(); + manager.PipelineDelegate = pipeline; + + Debug.Assert(null != pipeline); + } + } + + value = manager.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + + try + { + // Build withing the scope + return pipeline(ref context); + } + catch + { + manager.Recover(); + throw; + } + }; + } + + private ResolveDelegate PipelineFromRegistrationPerResolve(Type type, ExplicitRegistration registration) + { + ResolveDelegate? pipeline = null; + Debug.Assert(null != registration.LifetimeManager); + + return (ref BuilderContext context) => + { + object value; + LifetimeManager? lifetime; + + if (null != pipeline) + { + if (null != (lifetime = (LifetimeManager?)context.Get(typeof(LifetimeManager)))) + { + value = lifetime.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + } + + return pipeline(ref context); + } + + lock (registration) + { + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, registration); + + pipeline = builder.Pipeline(); + registration.LifetimeManager.PipelineDelegate = pipeline; + + Debug.Assert(null != pipeline); + } + } + + if (null != (lifetime = (LifetimeManager?)context.Get(typeof(LifetimeManager)))) + { + value = lifetime.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + } + + return pipeline(ref context); + }; + } + + private ResolveDelegate PipelineFromRegistrationDefault(Type type, ExplicitRegistration registration) + { + ResolveDelegate? pipeline = null; + Debug.Assert(null != registration.LifetimeManager); + + return (ref BuilderContext context) => + { + object value; + + if (null != pipeline) + { + value = registration.LifetimeManager.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + return pipeline(ref context); + } + + lock (registration) + { + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, registration); + + pipeline = builder.Pipeline(); + registration.LifetimeManager.PipelineDelegate = pipeline; + + Debug.Assert(null != pipeline); + } + } + + value = registration.LifetimeManager.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + + return pipeline(ref context); + }; + } + } +} diff --git a/src/UnityContainer/Pipeline/FromTypeFactory.cs b/src/UnityContainer/Pipeline/FromTypeFactory.cs new file mode 100644 index 00000000..d6b3c93c --- /dev/null +++ b/src/UnityContainer/Pipeline/FromTypeFactory.cs @@ -0,0 +1,97 @@ +using System.Diagnostics; +using System.Threading; +using Unity.Builder; +using Unity.Resolution; +using Unity.Storage; + +namespace Unity +{ + public partial class UnityContainer + { + private ResolveDelegate PipelineFromTypeFactory(ref HashKey key, UnityContainer container, TypeFactoryDelegate typeFactory) + { + Debug.Assert(null != _registry); + Debug.Assert(null != key.Type); + + var type = key.Type; + var name = key.Name; + var owner = this; + + int count = -1; + int position = 0; + var collisions = 0; + ResolveDelegate? pipeline = null; + + // Add Pipeline to the Registry + lock (_syncLock) + { + var targetBucket = key.HashCode % _registry.Buckets.Length; + for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next) + { + ref var candidate = ref _registry.Entries[i]; + if (candidate.Key != key) + { + collisions++; + continue; + } + + Debug.Assert(null != candidate.Pipeline); + + // Has already been created + return candidate.Pipeline; + } + + // Expand if required + if (_registry.RequireToGrow || CollisionsCutPoint < collisions) + { + _registry = new Registry(_registry); + targetBucket = key.HashCode % _registry.Buckets.Length; + } + + // Create new entry + ref var entry = ref _registry.Entries[_registry.Count]; + entry.Key = key; + entry.Type = type; + entry.Pipeline = BuildPipeline; + entry.Next = _registry.Buckets[targetBucket]; + position = _registry.Count++; + _registry.Buckets[targetBucket] = position; + } + + // Return temporary pipeline + return BuildPipeline; + + + // Create pipeline and add to Registry + object? BuildPipeline(ref BuilderContext context) + { + // Wait for right moment + while (0 != Interlocked.Increment(ref count)) + { + Interlocked.Decrement(ref count); +#if NETSTANDARD1_0 || NETCOREAPP1_0 + for (var i = 0; i < 100; i++) ; +#else + Thread.SpinWait(100); +#endif + } + + try + { + // Create if required + if (null == pipeline) + { + pipeline = typeFactory(type, container); + Debug.Assert(null != pipeline); + } + + return pipeline(ref context); + } + finally + { + Interlocked.Decrement(ref count); + } + }; + } + } +} diff --git a/src/UnityContainer/Pipeline/FromUnregisteredType.cs b/src/UnityContainer/Pipeline/FromUnregisteredType.cs new file mode 100644 index 00000000..61331c0d --- /dev/null +++ b/src/UnityContainer/Pipeline/FromUnregisteredType.cs @@ -0,0 +1,172 @@ +using System; +using System.Diagnostics; +using Unity.Builder; +using Unity.Lifetime; +using Unity.Resolution; +using Unity.Storage; + +namespace Unity +{ + public partial class UnityContainer + { + private ResolveDelegate PipelineFromUnregisteredType(ref HashKey key) + { + Debug.Assert(null != _registry); + Debug.Assert(null != key.Type); + + LifetimeManager manager; + + lock (_syncLock) + { + + var collisions = 0; + var targetBucket = key.HashCode % _registry.Buckets.Length; + for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next) + { + ref var candidate = ref _registry.Entries[i]; + if (candidate.Key != key) + { + collisions++; + continue; + } + + // TODO: Check if null pipeline is possible + if (null == candidate.Pipeline) + { + // Create or get Lifetime Manager + manager = Context.TypeLifetimeManager.CreateLifetimePolicy(); + manager.PipelineDelegate = manager switch + { + TransientLifetimeManager transient => PipelineFromUnregisteredTypeTransient(key.Type, transient, _registry.Count), + PerResolveLifetimeManager peresolve => PipelineFromUnregisteredTypePerResolve(key.Type, peresolve), + _ => PipelineFromUnregisteredTypeDefault(key.Type, manager) + }; + + candidate.Pipeline = manager.Pipeline; + } + + // Replaced registration + return candidate.Pipeline; + } + + // Expand if required + if (_registry.RequireToGrow || CollisionsCutPoint < collisions) + { + _registry = new Registry(_registry); + targetBucket = key.HashCode % _registry.Buckets.Length; + } + + + // Create a Lifetime Manager + manager = Context.TypeLifetimeManager.CreateLifetimePolicy(); + manager.PipelineDelegate = manager switch + { + TransientLifetimeManager transient => PipelineFromUnregisteredTypeTransient(key.Type, transient, _registry.Count), + PerResolveLifetimeManager peresolve => PipelineFromUnregisteredTypePerResolve(key.Type, peresolve), + _ => PipelineFromUnregisteredTypeDefault(key.Type, manager) + }; + + Debug.Assert(null != key.Type); + + // Create new entry + ref var entry = ref _registry.Entries[_registry.Count]; + entry.Key = key; + entry.Next = _registry.Buckets[targetBucket]; + entry.Type = key.Type; + entry.IsExplicit = true; + entry.Pipeline = manager.Pipeline; + int position = _registry.Count++; + _registry.Buckets[targetBucket] = position; + } + + return manager.Pipeline; + } + + private ResolveDelegate PipelineFromUnregisteredTypeTransient(Type type, TransientLifetimeManager manager, int position) + { + ResolveDelegate? pipeline = null; + + return (ref BuilderContext context) => + { + lock (manager) + { + // Create if required + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache); + pipeline = builder.Pipeline(); + + Debug.Assert(null != pipeline); + Debug.Assert(null != _registry); + + // Replace pipeline in storage + lock (_syncLock) + { + _registry.Entries[position].Pipeline = pipeline; + } + } + + return pipeline(ref context); + } + }; + } + + private ResolveDelegate PipelineFromUnregisteredTypePerResolve(Type type, PerResolveLifetimeManager manager) + { + ResolveDelegate? pipeline = null; + + return (ref BuilderContext context) => + { + lock (manager) + { + // Create if required + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache); + pipeline = builder.Pipeline(); + + Debug.Assert(null != pipeline); + + manager.PipelineDelegate = pipeline; + return pipeline(ref context); + } + + var lifetime = (LifetimeManager?)context.Get(typeof(LifetimeManager)); + Debug.Assert(null != lifetime); + + var value = lifetime.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + + return pipeline(ref context); + } + }; + } + + private ResolveDelegate PipelineFromUnregisteredTypeDefault(Type type, LifetimeManager manager) + { + ResolveDelegate? pipeline = null; + + return (ref BuilderContext context) => + { + lock (manager) + { + // Create if required + if (null == pipeline) + { + PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache); + pipeline = builder.Pipeline(); + + Debug.Assert(null != pipeline); + + manager.PipelineDelegate = pipeline; + } + + var value = manager.Get(LifetimeContainer); + if (LifetimeManager.NoValue != value) return value; + + return pipeline(ref context); + } + }; + } + } +} diff --git a/src/UnityContainer.Public.cs b/src/UnityContainer/Public.cs similarity index 99% rename from src/UnityContainer.Public.cs rename to src/UnityContainer/Public.cs index 6aa29bce..92bfe248 100644 --- a/src/UnityContainer.Public.cs +++ b/src/UnityContainer/Public.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; -using System.Threading.Tasks; using Unity.Builder; using Unity.Events; using Unity.Extension; diff --git a/src/UnityContainer.Registration.cs b/src/UnityContainer/Registration/Registration.cs similarity index 100% rename from src/UnityContainer.Registration.cs rename to src/UnityContainer/Registration/Registration.cs diff --git a/src/UnityContainer/Resolution/Array.cs b/src/UnityContainer/Resolution/Array.cs new file mode 100644 index 00000000..4c14a07a --- /dev/null +++ b/src/UnityContainer/Resolution/Array.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using Unity.Builder; +using Unity.Resolution; + +namespace Unity +{ + public partial class UnityContainer + { + } +} diff --git a/src/UnityContainer/Resolution/Enumerable.cs b/src/UnityContainer/Resolution/Enumerable.cs new file mode 100644 index 00000000..0d593fa0 --- /dev/null +++ b/src/UnityContainer/Resolution/Enumerable.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using Unity.Builder; +using Unity.Resolution; +using Unity.Storage; +using Unity.Utility; + +namespace Unity +{ + public partial class UnityContainer + { + #region Fields + + private static readonly MethodInfo EnumerableMethod = + typeof(UnityContainer).GetTypeInfo() + .GetDeclaredMethod(nameof(UnityContainer.EnumerableHandler)); + + private static readonly MethodInfo EnumerableFactory = + typeof(UnityContainer).GetTypeInfo() + .GetDeclaredMethod(nameof(UnityContainer.ResolverFactory)); + + #endregion + + + #region TypeResolverFactory + + public static TypeFactoryDelegate EnumerableTypeFactory = (Type type, UnityContainer container) => + { +#if NETSTANDARD1_0 || NETCOREAPP1_0 || NET40 + var typeArgument = type.GetTypeInfo().GenericTypeArguments.First(); + if (typeArgument.GetTypeInfo().IsGenericType) +#else + Debug.Assert(0 < type.GenericTypeArguments.Length); + var typeArgument = type.GenericTypeArguments.First(); + if (typeArgument.IsGenericType) +#endif + { + return ((EnumerableFactoryDelegate) + EnumerableFactory.MakeGenericMethod(typeArgument) + .CreateDelegate(typeof(EnumerableFactoryDelegate)))(); + } + else + { + return (ResolveDelegate) + EnumerableMethod.MakeGenericMethod(typeArgument) + .CreateDelegate(typeof(ResolveDelegate)); + } + }; + + #endregion + + + #region Implementation + + private static object EnumerableHandler(ref BuilderContext context) + { + return ((UnityContainer)context.Container).ResolveEnumerable(context.Resolve, + context.Name); + } + + private static ResolveDelegate ResolverFactory() + { + Type type = typeof(TElement).GetGenericTypeDefinition(); + return (ref BuilderContext c) => ((UnityContainer)c.Container).ResolveEnumerable(c.Resolve, type, c.Name); + } + + #endregion + + + #region Nested Types + + private delegate ResolveDelegate EnumerableFactoryDelegate(); + + #endregion + + + #region Enumerator + + private class EnumerableEnumerator : IEnumerator + { + #region Fields + + private int _prime = 5; + private int[] Buckets; + private Entry[] Entries; + UnityContainer _container; + private int Count; + + #endregion + + + #region Constructors + + public EnumerableEnumerator(UnityContainer container) + { + _container = container; + var size = HashHelpers.Primes[_prime]; + Buckets = new int[size]; + Entries = new Entry[size]; + +#if !NET40 + unsafe + { + fixed (int* bucketsPtr = Buckets) + { + int* ptr = bucketsPtr; + var end = bucketsPtr + Buckets.Length; + while (ptr < end) *ptr++ = -1; + } + } +#else + for(int i = 0; i < Buckets.Length; i++) + Buckets[i] = -1; +#endif + } + + #endregion + + + #region IEnumerator + + public TType Current => throw new NotImplementedException(); + + object IEnumerator.Current => throw new NotImplementedException(); + + public void Dispose() + { + throw new NotImplementedException(); + } + + public bool MoveNext() + { + throw new NotImplementedException(); + } + + public void Reset() + { + throw new NotImplementedException(); + } + + #endregion + } + + #endregion + + + #region Entry Type + + private struct Entry + { + public HashKey Key; + public int Next; + } + + #endregion + } +} diff --git a/src/UnityContainer.Resolution.cs b/src/UnityContainer/Resolution/Resolution.cs similarity index 95% rename from src/UnityContainer.Resolution.cs rename to src/UnityContainer/Resolution/Resolution.cs index f86a6b2a..09e2a26b 100644 --- a/src/UnityContainer.Resolution.cs +++ b/src/UnityContainer/Resolution/Resolution.cs @@ -263,7 +263,7 @@ namespace Unity return argType; } - internal IEnumerable ResolveArray(Func resolve, Type type) + internal IEnumerable ResolveArray(Func resolve, Type type) { object? value; var set = new QuickSet(); @@ -292,7 +292,7 @@ namespace Unity { try { - value = resolve(typeof(TElement), registration); + value = resolve(typeof(TElement), registration.Name); } catch (ArgumentException ex) when (ex.InnerException is TypeLoadException) { @@ -308,7 +308,7 @@ namespace Unity } } - internal IEnumerable ResolveArray(Func resolve, + internal IEnumerable ResolveArray(Func resolve, Type type, Type typeDefinition) { object? value; @@ -340,7 +340,7 @@ namespace Unity { try { - value = resolve(typeof(TElement), registration); + value = resolve(typeof(TElement), registration.Name); } catch (ArgumentException ex) when (ex.InnerException is TypeLoadException) { @@ -367,9 +367,7 @@ namespace Unity { try { - var itemKey = new HashKey(typeof(TElement), registration.Name); - var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name, registration); - value = resolve(typeof(TElement), item); + value = resolve(typeof(TElement), registration.Name); } catch (MakeGenericTypeFailedException) { continue; } catch (InvalidRegistrationException) { continue; } @@ -383,7 +381,7 @@ namespace Unity } } - internal IEnumerable ComplexArray(Func resolve, Type type) + internal IEnumerable ComplexArray(Func resolve, Type type) { object? value; var set = new QuickSet(); @@ -413,9 +411,7 @@ namespace Unity { try { - var itemKey = new HashKey(typeof(TElement), registration.Name); - var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name, registration); - value = resolve(typeof(TElement), item); + value = resolve(typeof(TElement), registration.Name); } catch (ArgumentException ex) when (ex.InnerException is TypeLoadException) { @@ -431,7 +427,7 @@ namespace Unity } } - internal IEnumerable ComplexArray(Func resolve, + internal IEnumerable ComplexArray(Func resolve, Type type, Type typeDefinition) { object? value; @@ -462,9 +458,7 @@ namespace Unity { try { - var itemKey = new HashKey(typeof(TElement), registration.Name); - var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name); - value = resolve(typeof(TElement), item); + value = resolve(typeof(TElement), registration.Name); } catch (ArgumentException ex) when (ex.InnerException is TypeLoadException) { @@ -491,10 +485,8 @@ namespace Unity { try { - var itemKey = new HashKey(typeof(TElement), registration.Name); - var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name); #pragma warning disable CS8601 // Possible null reference assignment. - value = (TElement)resolve(typeof(TElement), item); + value = (TElement)resolve(typeof(TElement), registration.Name); #pragma warning restore CS8601 // Possible null reference assignment. } catch (MakeGenericTypeFailedException) { continue; } diff --git a/src/Utility/ExtensionExtensions.cs b/src/UnityContainer/Utility/ExtensionExtensions.cs similarity index 100% rename from src/Utility/ExtensionExtensions.cs rename to src/UnityContainer/Utility/ExtensionExtensions.cs diff --git a/src/Utility/ModeFlagsExtensions.cs b/src/UnityContainer/Utility/ModeFlagsExtensions.cs similarity index 100% rename from src/Utility/ModeFlagsExtensions.cs rename to src/UnityContainer/Utility/ModeFlagsExtensions.cs