Skip to content

Commit 16a7730

Browse files
committed
Replace TinyIoC with Microsoft dependency injection
1 parent 2dd1d8e commit 16a7730

9 files changed

Lines changed: 390 additions & 4310 deletions

File tree

Lines changed: 196 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,217 @@
11
using System;
2-
using TinyIoC;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.Extensions.DependencyInjection;
35

46
namespace Exceptionless.Dependency {
57
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+
}
730

831
public object Resolve(Type serviceType) {
932
if (serviceType == null)
1033
throw new ArgumentNullException(nameof(serviceType));
1134

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+
}
1345
}
1446

1547
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+
}
1773
}
1874

1975
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+
}
21129
}
22130

23131
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+
}
25215
}
26216
}
27217
}

src/Exceptionless/Dependency/DependencyResolver.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Exceptionless.Services;
66
using Exceptionless.Storage;
77
using Exceptionless.Submission;
8+
using Microsoft.Extensions.DependencyInjection;
89

910
namespace Exceptionless.Dependency {
1011
public class DependencyResolver {
@@ -23,9 +24,35 @@ public static IDependencyResolver CreateDefault() {
2324
return resolver;
2425
}
2526

27+
/// <summary>
28+
/// Creates a resolver containing the default Exceptionless services followed by application overrides.
29+
/// </summary>
30+
/// <param name="services">Services that should be added to or override the defaults.</param>
31+
public static IDependencyResolver CreateDefault(IServiceCollection services) {
32+
if (services == null)
33+
throw new ArgumentNullException(nameof(services));
34+
35+
var resolver = (DefaultDependencyResolver)CreateDefault();
36+
resolver.AddServices(services);
37+
return resolver;
38+
}
39+
2640
public static void RegisterDefaultServices(IDependencyResolver resolver) {
2741
resolver.Register(resolver);
2842

43+
if (resolver is DefaultDependencyResolver defaultResolver) {
44+
resolver.Register<IObjectStorage, InMemoryObjectStorage>();
45+
resolver.Register<IExceptionlessLog, NullExceptionlessLog>();
46+
resolver.Register<IJsonSerializer, DefaultJsonSerializer>();
47+
resolver.Register<IStorageSerializer, DefaultJsonSerializer>();
48+
resolver.Register<IEventQueue, DefaultEventQueue>();
49+
resolver.Register<ISubmissionClient, DefaultSubmissionClient>();
50+
resolver.Register<IEnvironmentInfoCollector, DefaultEnvironmentInfoCollector>();
51+
resolver.Register<ILastReferenceIdManager, DefaultLastReferenceIdManager>();
52+
defaultResolver.RegisterSingleton(typeof(PersistedDictionary), () => new PersistedDictionary("client-data.json", resolver.Resolve<IObjectStorage>(), resolver.Resolve<IJsonSerializer>()));
53+
return;
54+
}
55+
2956
var fileStorage = new Lazy<IObjectStorage>(() => resolver.Resolve<InMemoryObjectStorage>());
3057
resolver.Register(typeof(IObjectStorage), () => fileStorage.Value);
3158

src/Exceptionless/Dependency/DependencyResolverExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ public static void Register<TService>(this IDependencyResolver resolver, TServic
4545
if (resolver == null)
4646
throw new ArgumentNullException(nameof(resolver));
4747

48+
if (resolver is DefaultDependencyResolver defaultResolver) {
49+
defaultResolver.RegisterInstance(typeof(TService), implementation);
50+
return;
51+
}
52+
4853
resolver.Register(typeof(TService), () => implementation);
4954
}
5055

0 commit comments

Comments
 (0)