diff --git a/src/Aggregate.cs b/src/Aggregate.cs deleted file mode 100644 index 854d8f7..0000000 --- a/src/Aggregate.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Unity.Injection; - -namespace Unity.Microsoft.DependencyInjection -{ - public class Aggregate - { - public Type Type { get; private set; } - - private List Services { get; set; } = new List(); - private ServiceDescriptor Last; - private IUnityContainer Container; - - public Aggregate(Type type, IUnityContainer container) - { - Type = type; - Container = container; - } - - public void AddService(ServiceDescriptor service) - { - Services.Add(service); - Last = service; - } - - public void Register() - { - //foreach (var serv in Services) - //{ - // var qualifier = serv.GetImplementationType().FullName; - // Container.Register(serv, qualifier); - //} - - //Container.RegisterType(Type, Last.GetLifetime(), - // new InjectionFactory((c, t, s) => - // { - // if (Last.ServiceType.GetTypeInfo().IsGenericTypeDefinition) - // return c.Resolve(t, Last.GetImplementationType().FullName); - // var instance = Resolve(c); - // return instance; - // })); - - //var enumType = typeof(IEnumerable<>).MakeGenericType(Type); - //Container.RegisterType(enumType, new HierarchicalTransientLifetimeManager(), - // new InjectionFactory(c => - // { - // List instances = new List(); - // foreach (var serv in Services) - // { - // if (!serv.ServiceType.GetTypeInfo().IsGenericTypeDefinition) - // { - // var qualifier = serv.GetImplementationType().FullName; - // var instance = Container.Resolve(serv.ServiceType, qualifier); - // instances.Add(instance); - // } - // } - // return typeof(Enumerable) - // .GetTypeInfo() - // .GetDeclaredMethod("Cast") - // .MakeGenericMethod(Type) - // .Invoke(null, new[] { instances }); - // })); - } - - public object Resolve(IUnityContainer container) - { - return container.Resolve(Type, Last.GetImplementationType().FullName); - } - } -} diff --git a/src/Configuration.cs b/src/Configuration.cs index ad2def7..09b7108 100644 --- a/src/Configuration.cs +++ b/src/Configuration.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Unity.Injection; using Unity.Lifetime; +using Unity.Microsoft.DependencyInjection.Lifetime; namespace Unity.Microsoft.DependencyInjection { @@ -13,7 +14,7 @@ internal static class Configuration internal static IUnityContainer AddServices(this IUnityContainer container, IServiceCollection services) { - var lifetime = container.Configure() + var lifetime = container.Configure() .Lifetime; foreach (var group in services.GroupBy(serviceDescriptor => serviceDescriptor.ServiceType, @@ -24,7 +25,7 @@ internal static IUnityContainer AddServices(this IUnityContainer container, ISer for (var i = 0; i < group.Length - 1; i++) { var descriptor = group[i]; - container.Register(descriptor, descriptor.GetRegistrationName(), lifetime); + container.Register(descriptor, Guid.NewGuid().ToString(), lifetime); } // Register default types @@ -79,7 +80,7 @@ internal static LifetimeManager GetLifetime(this ServiceDescriptor serviceDescri case ServiceLifetime.Scoped: return new HierarchicalLifetimeManager(); case ServiceLifetime.Singleton: - return new InjectionSingletonLifetimeManager(lifetime); + return new ContainerControlledLifetimeManager(); case ServiceLifetime.Transient: return new InjectionTransientLifetimeManager(); default: diff --git a/src/Lifetime/InjectionScopeLifetimeManager.cs b/src/Lifetime/InjectionScopeLifetimeManager.cs deleted file mode 100644 index d0c6387..0000000 --- a/src/Lifetime/InjectionScopeLifetimeManager.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; -using Unity.Exceptions; -using Unity.Lifetime; - -namespace Unity.Microsoft.DependencyInjection.Lifetime -{ - public class InjectionScopeLifetimeManager : HierarchicalLifetimeManager - { - } -} diff --git a/src/Lifetime/InjectionSingletonLifetimeManager.cs b/src/Lifetime/InjectionSingletonLifetimeManager.cs index 6acb69e..4e12918 100644 --- a/src/Lifetime/InjectionSingletonLifetimeManager.cs +++ b/src/Lifetime/InjectionSingletonLifetimeManager.cs @@ -1,18 +1,14 @@ using System; -using System.Threading; -using Unity.Exceptions; using Unity.Lifetime; -namespace Unity.Microsoft.DependencyInjection +namespace Unity.Microsoft.DependencyInjection.Lifetime { - public class InjectionSingletonLifetimeManager : LifetimeManager, IRequiresRecovery + public class InjectionSingletonLifetimeManager : ContainerControlledLifetimeManager { #region Fields - protected object _value; private ILifetimeContainer _lifetime; - private readonly object _lockObj = new object(); - + #endregion @@ -22,100 +18,36 @@ public InjectionSingletonLifetimeManager(ILifetimeContainer lifetime) } - /// - /// Retrieve a value from the backing store associated with this Lifetime policy. - /// - /// the object desired, or null if no such object is currently stored. - /// Calls to this method acquire a lock which is released only if a non-null value - /// has been set for the lifetime manager. - public override object GetValue(ILifetimeContainer container = null) + protected override void SynchronizedSetValue(object newValue, ILifetimeContainer container = null) { - Monitor.Enter(_lockObj); - var result = SynchronizedGetValue(container); - if (result != null) - { - Monitor.Exit(_lockObj); - } - return result; + base.SynchronizedSetValue(newValue, container); + _lifetime.Add(new DisposableAction(() => RemoveValue(_lifetime))); } - /// - /// Performs the actual retrieval of a value from the backing store associated - /// with this Lifetime policy. - /// - /// the object desired, or null if no such object is currently stored. - /// This method is invoked by - /// after it has acquired its lock. - protected virtual object SynchronizedGetValue(ILifetimeContainer container) + protected override LifetimeManager OnCreateLifetimeManager() { - return _value; + return new InjectionSingletonLifetimeManager(_lifetime); } - /// - /// Stores the given value into backing store for retrieval later. - /// - /// The object being stored. - /// The container this value belongs to. - /// Setting a value will attempt to release the lock acquired by - /// . - public override void SetValue(object newValue, ILifetimeContainer container = null) - { - SynchronizedSetValue(newValue, container); - TryExit(); - } - - /// - /// Performs the actual storage of the given value into backing store for retrieval later. - /// - /// The object being stored. - /// - /// This method is invoked by - /// before releasing its lock. - protected virtual void SynchronizedSetValue(object newValue, ILifetimeContainer container) - { - _value = newValue; - if (_value is IDisposable disposable) _lifetime.Add(disposable); - } + #region Nested Types - /// - /// A method that does whatever is needed to clean up - /// as part of cleaning up after an exception. - /// - /// - /// Don't do anything that could throw in this method, - /// it will cause later recover operations to get skipped - /// and play real havoc with the stack trace. - /// - public void Recover() + private class DisposableAction : IDisposable { - TryExit(); - } + private readonly Action _action; - protected virtual void TryExit() - { -#if !NET40 - // Prevent first chance exception when abandoning a lock that has not been entered - if (!Monitor.IsEntered(_lockObj)) return; -#endif - try + public DisposableAction(Action action) { - Monitor.Exit(_lockObj); + _action = action ?? throw new ArgumentNullException(nameof(action)); } - catch (SynchronizationLockException) + + public void Dispose() { - // Noop here - we don't hold the lock and that's ok. + _action(); } } - public override void RemoveValue(ILifetimeContainer container = null) - { - TryExit(); - } + #endregion - protected override LifetimeManager OnCreateLifetimeManager() - { - return new InjectionSingletonLifetimeManager(_lifetime); - } } } diff --git a/src/Lifetime/InjectionTransientLifetimeManager.cs b/src/Lifetime/InjectionTransientLifetimeManager.cs index 7299238..fcd0579 100644 --- a/src/Lifetime/InjectionTransientLifetimeManager.cs +++ b/src/Lifetime/InjectionTransientLifetimeManager.cs @@ -1,7 +1,7 @@ using System; using Unity.Lifetime; -namespace Unity.Microsoft.DependencyInjection +namespace Unity.Microsoft.DependencyInjection.Lifetime { /// /// A special lifetime manager which works like , diff --git a/src/Extension/MDIExtension.cs b/src/MDIExtension.cs similarity index 77% rename from src/Extension/MDIExtension.cs rename to src/MDIExtension.cs index d4027d3..49080d3 100644 --- a/src/Extension/MDIExtension.cs +++ b/src/MDIExtension.cs @@ -1,10 +1,11 @@ using Unity.Extension; using Unity.Lifetime; +using Unity.Microsoft.DependencyInjection.Policy; using Unity.Policy; namespace Unity.Microsoft.DependencyInjection { - internal class MDIExtension : UnityContainerExtension + internal class MdiExtension : UnityContainerExtension { protected override void Initialize() { diff --git a/src/Policy/ConstructorSelectorPolicy.cs b/src/Policy/ConstructorSelectorPolicy.cs index 40e0c50..034adf7 100644 --- a/src/Policy/ConstructorSelectorPolicy.cs +++ b/src/Policy/ConstructorSelectorPolicy.cs @@ -2,18 +2,18 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using Unity.Policy; -using Unity.ObjectBuilder.BuildPlan.Selection; +using Unity.Attributes; using Unity.Builder; using Unity.Builder.Selection; -using Unity.Attributes; +using Unity.ObjectBuilder.BuildPlan.Selection; +using Unity.Policy; using Unity.ResolverPolicy; -namespace Unity.Microsoft.DependencyInjection +namespace Unity.Microsoft.DependencyInjection.Policy { public class ConstructorSelectorPolicy : IConstructorSelectorPolicy { - DefaultUnityConstructorSelectorPolicy dependency = new DefaultUnityConstructorSelectorPolicy(); + private readonly DefaultUnityConstructorSelectorPolicy _dependency = new DefaultUnityConstructorSelectorPolicy(); /// /// Choose the constructor to call for the given type. @@ -28,7 +28,7 @@ public SelectedConstructor SelectConstructor(IBuilderContext context, IPolicyLis ConstructorInfo ctor = FindDependencyConstructor(context); if (ctor != null) return CreateSelectedConstructor(ctor); - return dependency.SelectConstructor(context, resolverPolicyDestination); + return _dependency.SelectConstructor(context, resolverPolicyDestination); } private ConstructorInfo FindDependencyConstructor(IBuilderContext context) @@ -57,6 +57,7 @@ private static ConstructorInfo FindSingleConstructor(IEnumerable configurationAction = null) - { - return services.AddSingleton>(new ServiceProviderFactory(configurationAction)); - } - } -} diff --git a/src/ServiceDescriptorExtensions.cs b/src/ServiceDescriptorExtensions.cs deleted file mode 100644 index 814d90f..0000000 --- a/src/ServiceDescriptorExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; -using Unity.Lifetime; - -namespace Unity.Microsoft.DependencyInjection -{ - internal static class ServiceDescriptorExtensions - { - internal static Type GetImplementationType(this ServiceDescriptor service) - { - if (service.ImplementationType != null) - { - return service.ImplementationType; - } - else if (service.ImplementationInstance != null) - { - return service.ImplementationInstance.GetType(); - } - else if (service.ImplementationFactory != null) - { - var typeArguments = service.ImplementationFactory.GetType().GenericTypeArguments; - return typeArguments[1]; - } - return null; - } - - internal static string GetRegistrationName(this ServiceDescriptor service) - { - if (service.ImplementationType != null) - { - return service.ImplementationType.FullName; - } - else if (service.ImplementationInstance != null) - { - return service.ImplementationInstance.GetType().FullName; - } - else if (service.ImplementationFactory != null) - { - var typeArguments = service.ImplementationFactory.GetType().GenericTypeArguments; - return typeArguments[1].FullName; - } - - return null; - } - } -} diff --git a/src/ServiceProvider.cs b/src/ServiceProvider.cs index 45547fb..cca9976 100644 --- a/src/ServiceProvider.cs +++ b/src/ServiceProvider.cs @@ -3,9 +3,12 @@ namespace Unity.Microsoft.DependencyInjection { - public class ServiceProvider : IServiceProvider, IServiceScopeFactory, IServiceScope, IDisposable + public class ServiceProvider : IServiceProvider, + IServiceScopeFactory, + IServiceScope, + IDisposable { - protected IUnityContainer _container; + private IUnityContainer _container; internal ServiceProvider(IUnityContainer container) @@ -37,7 +40,7 @@ public object GetService(Type serviceType) public IServiceScope CreateScope() { return new ServiceProvider(_container.CreateChildContainer() - .AddNewExtension()); + .AddNewExtension()); } #endregion @@ -54,7 +57,7 @@ public IServiceScope CreateScope() public static IServiceProvider ConfigureServices(IServiceCollection services) { - return new ServiceProvider(new UnityContainer().AddNewExtension() + return new ServiceProvider(new UnityContainer().AddNewExtension() .AddServices(services)); } @@ -90,8 +93,14 @@ public static class ServiceProviderExtension public static IServiceProvider ConfigureServices(this IUnityContainer container, IServiceCollection services) { return new ServiceProvider(container.CreateChildContainer() - .AddNewExtension() + .AddNewExtension() .AddServices(services)); } + + public static IServiceCollection AddUnity(this IServiceCollection services, Action configurationAction = null) + { + return services.AddSingleton>(new ServiceProviderFactory(configurationAction)); + } + } } \ No newline at end of file diff --git a/tests/UnityDependencyInjectionTests.cs b/tests/UnityDependencyInjectionTests.cs index 685663c..0d9e4f5 100644 --- a/tests/UnityDependencyInjectionTests.cs +++ b/tests/UnityDependencyInjectionTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; @@ -15,33 +16,38 @@ protected override IServiceProvider CreateServiceProvider(IServiceCollection ser return ServiceProvider.ConfigureServices(serviceCollection); } + [Fact] public void Disposes_InReverseOrderOfCreation() { - TestServiceCollection serviceCollection = new TestServiceCollection(); - ServiceCollectionServiceExtensions.AddSingleton(serviceCollection); - ServiceCollectionServiceExtensions.AddTransient(serviceCollection); - ServiceCollectionServiceExtensions.AddSingleton(serviceCollection); - ServiceCollectionServiceExtensions.AddScoped(serviceCollection); - ServiceCollectionServiceExtensions.AddTransient(serviceCollection); - ServiceCollectionServiceExtensions.AddSingleton(serviceCollection); - IServiceProvider provider1 = this.CreateServiceProvider(serviceCollection); - FakeDisposeCallback callback = ServiceProviderServiceExtensions.GetService(provider1); - IFakeOuterService service = ServiceProviderServiceExtensions.GetService(provider1); - ((IDisposable)provider1).Dispose(); - Assert.Equal(service, callback.Disposed[0]); - Assert.Equal(Enumerable.Reverse(service.MultipleServices), - Enumerable.OfType(Enumerable.Take(Enumerable.Skip((IEnumerable)callback.Disposed, 1), 3))); - Assert.Equal(service.SingleService, callback.Disposed[4]); + + // Arrange + var serviceCollection = new TestServiceCollection(); + serviceCollection.AddSingleton(); + serviceCollection.AddTransient(); + serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddTransient(); + serviceCollection.AddSingleton(); + var serviceProvider = CreateServiceProvider(serviceCollection); + + var callback = serviceProvider.GetService(); + var outer = serviceProvider.GetService(); + var multipleServices = outer.MultipleServices.ToArray(); + + // Act + ((IDisposable)serviceProvider).Dispose(); + + // Assert + Assert.Equal(outer, callback.Disposed[0]); + Assert.Equal(multipleServices.Reverse(), callback.Disposed.Skip(1).Take(3).OfType()); + Assert.Equal(outer.SingleService, callback.Disposed[4]); + } - internal class TestServiceCollection : List, - IServiceCollection, - IList, - ICollection, - IEnumerable + + internal class TestServiceCollection : List, IServiceCollection, IList, ICollection, IEnumerable, IEnumerable { } - } } \ No newline at end of file