|
1 | 1 | using System; |
2 | | -using TinyIoC; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Linq; |
| 4 | +using Microsoft.Extensions.DependencyInjection; |
3 | 5 |
|
4 | 6 | namespace Exceptionless.Dependency { |
5 | 7 | public sealed class DefaultDependencyResolver : IDependencyResolver { |
6 | | - private readonly TinyIoCContainer _container = new TinyIoCContainer(); |
| 8 | + private readonly object _lock = new object(); |
| 9 | + private readonly IServiceCollection _services; |
| 10 | + private readonly List<ServiceProvider> _providers = new List<ServiceProvider>(); |
| 11 | + private ServiceProvider _provider; |
| 12 | + private bool _disposed; |
| 13 | + |
| 14 | + /// <summary> |
| 15 | + /// Creates an empty resolver backed by Microsoft.Extensions.DependencyInjection. |
| 16 | + /// </summary> |
| 17 | + public DefaultDependencyResolver() : this(new ServiceCollection()) { } |
| 18 | + |
| 19 | + /// <summary> |
| 20 | + /// Creates a resolver backed by a copy of the supplied service descriptors. |
| 21 | + /// </summary> |
| 22 | + /// <param name="services">Services to make available to the resolver.</param> |
| 23 | + public DefaultDependencyResolver(IServiceCollection services) { |
| 24 | + if (services == null) |
| 25 | + throw new ArgumentNullException(nameof(services)); |
| 26 | + |
| 27 | + _services = new ServiceCollection(); |
| 28 | + AddServices(services); |
| 29 | + } |
7 | 30 |
|
8 | 31 | public object Resolve(Type serviceType) { |
9 | 32 | if (serviceType == null) |
10 | 33 | throw new ArgumentNullException(nameof(serviceType)); |
11 | 34 |
|
12 | | - return _container.Resolve(serviceType); |
| 35 | + lock (_lock) { |
| 36 | + ThrowIfDisposed(); |
| 37 | + |
| 38 | + var provider = GetProvider(); |
| 39 | + var service = provider.GetService(serviceType); |
| 40 | + if (service != null) |
| 41 | + return service; |
| 42 | + |
| 43 | + return CanActivate(serviceType) ? CreateInstance(provider, serviceType) : null; |
| 44 | + } |
13 | 45 | } |
14 | 46 |
|
15 | 47 | public void Register(Type serviceType, Type concreteType) { |
16 | | - _container.Register(serviceType, concreteType); |
| 48 | + if (serviceType == null) |
| 49 | + throw new ArgumentNullException(nameof(serviceType)); |
| 50 | + if (concreteType == null) |
| 51 | + throw new ArgumentNullException(nameof(concreteType)); |
| 52 | + if (!CanAssign(serviceType, concreteType)) |
| 53 | + throw new ArgumentException($"Type '{concreteType.FullName}' cannot be assigned to service '{serviceType.FullName}'.", nameof(concreteType)); |
| 54 | + |
| 55 | + lock (_lock) { |
| 56 | + ThrowIfDisposed(); |
| 57 | + Remove(serviceType); |
| 58 | + |
| 59 | + bool singleton = serviceType.IsInterface || serviceType.IsAbstract; |
| 60 | + if (serviceType.IsGenericTypeDefinition) { |
| 61 | + _services.Add(singleton |
| 62 | + ? ServiceDescriptor.Singleton(serviceType, concreteType) |
| 63 | + : ServiceDescriptor.Transient(serviceType, concreteType)); |
| 64 | + } else { |
| 65 | + Func<IServiceProvider, object> factory = provider => CreateInstance(provider, concreteType); |
| 66 | + _services.Add(singleton |
| 67 | + ? ServiceDescriptor.Singleton(serviceType, factory) |
| 68 | + : ServiceDescriptor.Transient(serviceType, factory)); |
| 69 | + } |
| 70 | + |
| 71 | + InvalidateProvider(); |
| 72 | + } |
17 | 73 | } |
18 | 74 |
|
19 | 75 | public void Register(Type serviceType, Func<object> activator) { |
20 | | - _container.Register(serviceType, (c, p) => activator()); |
| 76 | + if (serviceType == null) |
| 77 | + throw new ArgumentNullException(nameof(serviceType)); |
| 78 | + if (activator == null) |
| 79 | + throw new ArgumentNullException(nameof(activator)); |
| 80 | + |
| 81 | + lock (_lock) { |
| 82 | + ThrowIfDisposed(); |
| 83 | + Remove(serviceType); |
| 84 | + _services.Add(ServiceDescriptor.Transient(serviceType, _ => activator())); |
| 85 | + InvalidateProvider(); |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + public void RegisterInstance(Type serviceType, object instance) { |
| 90 | + if (serviceType == null) |
| 91 | + throw new ArgumentNullException(nameof(serviceType)); |
| 92 | + if (instance == null) |
| 93 | + throw new ArgumentNullException(nameof(instance)); |
| 94 | + if (!serviceType.IsInstanceOfType(instance)) |
| 95 | + throw new ArgumentException($"Instance of type '{instance.GetType().FullName}' cannot be assigned to service '{serviceType.FullName}'.", nameof(instance)); |
| 96 | + |
| 97 | + lock (_lock) { |
| 98 | + ThrowIfDisposed(); |
| 99 | + Remove(serviceType); |
| 100 | + _services.Add(ServiceDescriptor.Singleton(serviceType, instance)); |
| 101 | + InvalidateProvider(); |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + internal void RegisterSingleton(Type serviceType, Func<object> activator) { |
| 106 | + if (serviceType == null) |
| 107 | + throw new ArgumentNullException(nameof(serviceType)); |
| 108 | + if (activator == null) |
| 109 | + throw new ArgumentNullException(nameof(activator)); |
| 110 | + |
| 111 | + lock (_lock) { |
| 112 | + ThrowIfDisposed(); |
| 113 | + Remove(serviceType); |
| 114 | + _services.Add(ServiceDescriptor.Singleton(serviceType, _ => activator())); |
| 115 | + InvalidateProvider(); |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + internal void AddServices(IEnumerable<ServiceDescriptor> services) { |
| 120 | + if (services == null) |
| 121 | + throw new ArgumentNullException(nameof(services)); |
| 122 | + |
| 123 | + lock (_lock) { |
| 124 | + ThrowIfDisposed(); |
| 125 | + foreach (var service in services) |
| 126 | + _services.Add(service); |
| 127 | + InvalidateProvider(); |
| 128 | + } |
21 | 129 | } |
22 | 130 |
|
23 | 131 | public void Dispose() { |
24 | | - _container.Dispose(); |
| 132 | + lock (_lock) { |
| 133 | + if (_disposed) |
| 134 | + return; |
| 135 | + |
| 136 | + _disposed = true; |
| 137 | + foreach (var provider in _providers) |
| 138 | + provider.Dispose(); |
| 139 | + |
| 140 | + _providers.Clear(); |
| 141 | + _provider = null; |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + private ServiceProvider GetProvider() { |
| 146 | + if (_provider != null) |
| 147 | + return _provider; |
| 148 | + |
| 149 | + _provider = _services.BuildServiceProvider(); |
| 150 | + _providers.Add(_provider); |
| 151 | + return _provider; |
| 152 | + } |
| 153 | + |
| 154 | + private object CreateInstance(IServiceProvider provider, Type concreteType) { |
| 155 | + return ActivatorUtilities.CreateInstance(new FallbackServiceProvider(this, provider), concreteType); |
| 156 | + } |
| 157 | + |
| 158 | + private void Remove(Type serviceType) { |
| 159 | + for (int index = _services.Count - 1; index >= 0; index--) { |
| 160 | + if (_services[index].ServiceType == serviceType) |
| 161 | + _services.RemoveAt(index); |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + private void InvalidateProvider() { |
| 166 | + _provider = null; |
| 167 | + } |
| 168 | + |
| 169 | + private void ThrowIfDisposed() { |
| 170 | + if (_disposed) |
| 171 | + throw new ObjectDisposedException(nameof(DefaultDependencyResolver)); |
| 172 | + } |
| 173 | + |
| 174 | + private static bool CanActivate(Type type) { |
| 175 | + return !type.IsAbstract && !type.IsInterface && !type.ContainsGenericParameters; |
| 176 | + } |
| 177 | + |
| 178 | + private static bool CanAssign(Type serviceType, Type concreteType) { |
| 179 | + if (!serviceType.IsGenericTypeDefinition) |
| 180 | + return serviceType.IsAssignableFrom(concreteType); |
| 181 | + |
| 182 | + if (!concreteType.IsGenericTypeDefinition) |
| 183 | + return false; |
| 184 | + |
| 185 | + if (serviceType.IsInterface) |
| 186 | + return concreteType.GetInterfaces().Any(type => type.IsGenericType && type.GetGenericTypeDefinition() == serviceType); |
| 187 | + |
| 188 | + for (var current = concreteType; current != null; current = current.BaseType) { |
| 189 | + if (current.IsGenericType && current.GetGenericTypeDefinition() == serviceType) |
| 190 | + return true; |
| 191 | + } |
| 192 | + |
| 193 | + return false; |
| 194 | + } |
| 195 | + |
| 196 | + private sealed class FallbackServiceProvider : IServiceProvider { |
| 197 | + private readonly DefaultDependencyResolver _resolver; |
| 198 | + private readonly IServiceProvider _provider; |
| 199 | + |
| 200 | + public FallbackServiceProvider(DefaultDependencyResolver resolver, IServiceProvider provider) { |
| 201 | + _resolver = resolver; |
| 202 | + _provider = provider; |
| 203 | + } |
| 204 | + |
| 205 | + public object GetService(Type serviceType) { |
| 206 | + if (serviceType == typeof(IServiceProvider)) |
| 207 | + return this; |
| 208 | + |
| 209 | + var service = _provider.GetService(serviceType); |
| 210 | + if (service != null) |
| 211 | + return service; |
| 212 | + |
| 213 | + return CanActivate(serviceType) ? _resolver.CreateInstance(_provider, serviceType) : null; |
| 214 | + } |
25 | 215 | } |
26 | 216 | } |
27 | 217 | } |
0 commit comments