Skip to content

Commit 38f340b

Browse files
committed
Releasing v2.0.0
1 parent bb5e628 commit 38f340b

File tree

4 files changed

+100
-13
lines changed

4 files changed

+100
-13
lines changed

package.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22

33
<PropertyGroup>
4-
<Version>1.0.0</Version>
4+
<Version>2.0.0</Version>
55
<PackageReleaseNotes>This package is distributed as .NET Standard 1.0 package. It is compatible with Microsoft.Extensions.DependencyInjection.Abstractions specification.</PackageReleaseNotes>
66
</PropertyGroup>
77

src/Configuration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ internal static LifetimeManager GetLifetime(this ServiceDescriptor serviceDescri
8080
case ServiceLifetime.Scoped:
8181
return new HierarchicalLifetimeManager();
8282
case ServiceLifetime.Singleton:
83-
return new ContainerControlledLifetimeManager();
83+
return new InjectionSingletonLifetimeManager(lifetime);
8484
case ServiceLifetime.Transient:
8585
return new InjectionTransientLifetimeManager();
8686
default:

src/Lifetime/InjectionSingletonLifetimeManager.cs

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,123 @@
11
using System;
2+
using System.Threading;
3+
using Unity.Exceptions;
24
using Unity.Lifetime;
35

46
namespace Unity.Microsoft.DependencyInjection.Lifetime
57
{
6-
public class InjectionSingletonLifetimeManager : ContainerControlledLifetimeManager
8+
public class InjectionSingletonLifetimeManager : LifetimeManager, IRequiresRecovery
9+
710
{
811
#region Fields
912

10-
private ILifetimeContainer _lifetime;
11-
13+
private readonly object _lockObj = new object();
14+
private readonly ILifetimeContainer _container;
15+
private object _value;
16+
1217
#endregion
1318

1419

15-
public InjectionSingletonLifetimeManager(ILifetimeContainer lifetime)
20+
#region Constructors
21+
22+
public InjectionSingletonLifetimeManager(ILifetimeContainer container)
1623
{
17-
_lifetime = lifetime;
24+
_container = container ?? throw new ArgumentNullException(nameof(container));
1825
}
1926

27+
#endregion
28+
2029

21-
protected override void SynchronizedSetValue(object newValue, ILifetimeContainer container = null)
30+
#region LifetimeManager
31+
32+
/// <summary>
33+
/// Remove the given object from backing store.
34+
/// </summary>
35+
/// <param name="container">Instance of container</param>
36+
public override void RemoveValue(ILifetimeContainer container = null)
2237
{
23-
base.SynchronizedSetValue(newValue, container);
24-
_lifetime.Add(new DisposableAction(() => RemoveValue(_lifetime)));
38+
if (_value == null) return;
39+
if (_value is IDisposable disposable)
40+
{
41+
disposable.Dispose();
42+
}
43+
_value = null;
2544
}
2645

2746
protected override LifetimeManager OnCreateLifetimeManager()
2847
{
29-
return new InjectionSingletonLifetimeManager(_lifetime);
48+
return new InjectionSingletonLifetimeManager(_container);
49+
}
50+
51+
52+
/// <summary>
53+
/// Retrieve a value from the backing store associated with this Lifetime policy.
54+
/// </summary>
55+
/// <returns>the object desired, or null if no such object is currently stored.</returns>
56+
/// <remarks>Calls to this method acquire a lock which is released only if a non-null value
57+
/// has been set for the lifetime manager.</remarks>
58+
public override object GetValue(ILifetimeContainer container = null)
59+
{
60+
Monitor.Enter(_lockObj);
61+
if (_value != null)
62+
{
63+
Monitor.Exit(_lockObj);
64+
}
65+
return _value;
3066
}
3167

3268

69+
/// <summary>
70+
/// Stores the given value into backing store for retrieval later.
71+
/// </summary>
72+
/// <param name="newValue">The object being stored.</param>
73+
/// <param name="container">The container this value belongs to.</param>
74+
/// <remarks>Setting a value will attempt to release the lock acquired by
75+
/// <see cref="SynchronizedLifetimeManager.GetValue"/>.</remarks>
76+
public override void SetValue(object newValue, ILifetimeContainer container = null)
77+
{
78+
_value = newValue;
79+
if (_value is IDisposable) _container.Add(new DisposableAction(() => RemoveValue(_container)));
80+
TryExit();
81+
}
82+
83+
#endregion
84+
85+
86+
#region IRequiresRecovery
87+
88+
/// <summary>
89+
/// A method that does whatever is needed to clean up
90+
/// as part of cleaning up after an exception.
91+
/// </summary>
92+
/// <remarks>
93+
/// Don't do anything that could throw in this method,
94+
/// it will cause later recover operations to get skipped
95+
/// and play real havoc with the stack trace.
96+
/// </remarks>
97+
public void Recover()
98+
{
99+
TryExit();
100+
}
101+
102+
protected virtual void TryExit()
103+
{
104+
#if !NET40
105+
// Prevent first chance exception when abandoning a lock that has not been entered
106+
if (!Monitor.IsEntered(_lockObj)) return;
107+
#endif
108+
try
109+
{
110+
Monitor.Exit(_lockObj);
111+
}
112+
catch (SynchronizationLockException)
113+
{
114+
// Noop here - we don't hold the lock and that's ok.
115+
}
116+
}
117+
118+
#endregion
119+
120+
33121
#region Nested Types
34122

35123
private class DisposableAction : IDisposable
@@ -48,6 +136,5 @@ public void Dispose()
48136
}
49137

50138
#endregion
51-
52139
}
53140
}

tests/UnityDependencyInjectionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void Disposes_InReverseOrderOfCreation()
4040

4141
// Assert
4242
Assert.Equal(outer, callback.Disposed[0]);
43-
Assert.Equal(multipleServices.Reverse(), callback.Disposed.Skip(1).Take(3).OfType<IFakeMultipleService>());
43+
Assert.Equal(outer.MultipleServices.Reverse(), callback.Disposed.Skip(1).Take(3).OfType<IFakeMultipleService>());
4444
Assert.Equal(outer.SingleService, callback.Disposed[4]);
4545

4646
}

0 commit comments

Comments
 (0)