Skip to content

Commit 8cf5de5

Browse files
author
LoneWandererProductions
committed
last fixes
1 parent 76ba99e commit 8cf5de5

2 files changed

Lines changed: 33 additions & 20 deletions

File tree

Core.Inject/CoreInjector.cs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,33 @@
77
*/
88

99
using System;
10+
using System.Collections.Concurrent;
1011
using System.Collections.Generic;
1112
using System.Linq;
13+
using System.Threading;
1214

1315
namespace Core.Inject
1416
{
1517
/// <summary>
1618
/// A simple dependency injection container that handles the registration and resolution of services.
1719
/// Supports singleton, transient, scoped, and instance-based registrations.
1820
/// </summary>
19-
public sealed class CoreInjector
21+
public sealed class CoreInjector : IDisposable
2022
{
2123
/// <summary>
2224
/// Stores the registered service factories, indexed by service type.
2325
/// </summary>
2426
private readonly Dictionary<Type, Func<object>> _registrations = new();
2527

28+
/// <summary>
29+
/// The singleton instances
30+
/// </summary>
31+
private readonly ConcurrentDictionary<Type, object> _singletonInstances = new();
32+
2633
/// <summary>
2734
/// The current scope for managing scoped dependencies.
2835
/// </summary>
29-
private SimpleScope _currentScope;
36+
private readonly AsyncLocal<SimpleScope?> _currentScope = new();
3037

3138
/// <summary>
3239
/// Registers a singleton service with a specific implementation type.
@@ -35,16 +42,10 @@ public sealed class CoreInjector
3542
/// <typeparam name="TService">The service type.</typeparam>
3643
/// <typeparam name="TImplementation">The implementation type for the service.</typeparam>
3744
/// <exception cref="InvalidOperationException">Thrown if the service is already registered.</exception>
38-
public void RegisterSingleton<TService, TImplementation>() where TImplementation : TService, new()
45+
public void RegisterSingleton<TService, TImplementation>() where TImplementation : TService
3946
{
40-
if (_registrations.ContainsKey(typeof(TService)))
41-
{
42-
throw new InvalidOperationException(string.Format(CoreInjectResource.ErrorServiceRegisteredSingleTon,
43-
typeof(TService).Name));
44-
}
45-
46-
TImplementation instance = new();
47-
_registrations[typeof(TService)] = () => instance; // Always return the same instance
47+
_registrations[typeof(TService)] = () =>
48+
_singletonInstances.GetOrAdd(typeof(TService), _ => CreateInstance(typeof(TImplementation)));
4849
}
4950

5051
/// <summary>
@@ -109,12 +110,12 @@ public void RegisterInstance<TService>(TService instance)
109110
{
110111
_registrations[typeof(TService)] = () =>
111112
{
112-
if (_currentScope == null)
113+
if (_currentScope.Value == null)
113114
{
114115
throw new InvalidOperationException(CoreInjectResource.ErrorNoActiveScope);
115116
}
116117

117-
return _currentScope.Resolve<TService>(() => new TImplementation());
118+
return _currentScope.Value.Resolve<TService>(() => new TImplementation());
118119
};
119120
}
120121

@@ -143,9 +144,9 @@ public void RegisterTransient(Type serviceType, Type implementationType)
143144
/// <returns>The resolved service instance.</returns>
144145
public TService Resolve<TService>()
145146
{
146-
if (_currentScope != null && _registrations.ContainsKey(typeof(TService)))
147+
if (_currentScope.Value != null && _registrations.ContainsKey(typeof(TService)))
147148
{
148-
return _currentScope.Resolve<TService>(() => (TService)_registrations[typeof(TService)]());
149+
return _currentScope.Value.Resolve<TService>(() => (TService)_registrations[typeof(TService)]());
149150
}
150151

151152
return (TService)Resolve(typeof(TService));
@@ -157,7 +158,7 @@ public TService Resolve<TService>()
157158
/// </summary>
158159
public void BeginScope()
159160
{
160-
_currentScope = new SimpleScope();
161+
_currentScope.Value = new SimpleScope();
161162
}
162163

163164
/// <summary>
@@ -166,10 +167,22 @@ public void BeginScope()
166167
/// </summary>
167168
public void EndScope()
168169
{
169-
_currentScope?.Dispose();
170-
_currentScope = null;
170+
_currentScope.Value?.Dispose();
171+
_currentScope.Value = null;
171172
}
172173

174+
/// <summary>
175+
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
176+
/// </summary>
177+
public void Dispose()
178+
{
179+
// Only dispose things we actually created!
180+
foreach (var instance in _singletonInstances.Values)
181+
{
182+
(instance as IDisposable)?.Dispose();
183+
}
184+
_singletonInstances.Clear();
185+
}
173186

174187
/// <summary>
175188
/// Resolves a service by its type.

Core.Inject/SimpleScope.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// ReSharper disable MemberCanBeInternal
1010

1111
using System;
12-
using System.Collections.Generic;
12+
using System.Collections.Concurrent;
1313

1414
namespace Core.Inject
1515
{
@@ -23,7 +23,7 @@ public sealed class SimpleScope : IDisposable
2323
/// <summary>
2424
/// Stores instances of services that should persist within the current scope.
2525
/// </summary>
26-
private readonly Dictionary<Type, object> _scopedInstances = new();
26+
private readonly ConcurrentDictionary<Type, object> _scopedInstances = new();
2727

2828
/// <inheritdoc />
2929
/// <summary>

0 commit comments

Comments
 (0)