Skip to content

Commit

Permalink
Releasing v2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ENikS committed Jan 9, 2018
1 parent bb5e628 commit 38f340b
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<Version>1.0.0</Version>
<Version>2.0.0</Version>
<PackageReleaseNotes>This package is distributed as .NET Standard 1.0 package. It is compatible with Microsoft.Extensions.DependencyInjection.Abstractions specification.</PackageReleaseNotes>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ internal static LifetimeManager GetLifetime(this ServiceDescriptor serviceDescri
case ServiceLifetime.Scoped:
return new HierarchicalLifetimeManager();
case ServiceLifetime.Singleton:
return new ContainerControlledLifetimeManager();
return new InjectionSingletonLifetimeManager(lifetime);
case ServiceLifetime.Transient:
return new InjectionTransientLifetimeManager();
default:
Expand Down
107 changes: 97 additions & 10 deletions src/Lifetime/InjectionSingletonLifetimeManager.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,123 @@
using System;
using System.Threading;
using Unity.Exceptions;
using Unity.Lifetime;

namespace Unity.Microsoft.DependencyInjection.Lifetime
{
public class InjectionSingletonLifetimeManager : ContainerControlledLifetimeManager
public class InjectionSingletonLifetimeManager : LifetimeManager, IRequiresRecovery

{
#region Fields

private ILifetimeContainer _lifetime;

private readonly object _lockObj = new object();
private readonly ILifetimeContainer _container;
private object _value;

#endregion


public InjectionSingletonLifetimeManager(ILifetimeContainer lifetime)
#region Constructors

public InjectionSingletonLifetimeManager(ILifetimeContainer container)
{
_lifetime = lifetime;
_container = container ?? throw new ArgumentNullException(nameof(container));
}

#endregion


protected override void SynchronizedSetValue(object newValue, ILifetimeContainer container = null)
#region LifetimeManager

/// <summary>
/// Remove the given object from backing store.
/// </summary>
/// <param name="container">Instance of container</param>
public override void RemoveValue(ILifetimeContainer container = null)
{
base.SynchronizedSetValue(newValue, container);
_lifetime.Add(new DisposableAction(() => RemoveValue(_lifetime)));
if (_value == null) return;
if (_value is IDisposable disposable)
{
disposable.Dispose();
}
_value = null;
}

protected override LifetimeManager OnCreateLifetimeManager()
{
return new InjectionSingletonLifetimeManager(_lifetime);
return new InjectionSingletonLifetimeManager(_container);
}


/// <summary>
/// Retrieve a value from the backing store associated with this Lifetime policy.
/// </summary>
/// <returns>the object desired, or null if no such object is currently stored.</returns>
/// <remarks>Calls to this method acquire a lock which is released only if a non-null value
/// has been set for the lifetime manager.</remarks>
public override object GetValue(ILifetimeContainer container = null)
{
Monitor.Enter(_lockObj);
if (_value != null)
{
Monitor.Exit(_lockObj);
}
return _value;
}


/// <summary>
/// Stores the given value into backing store for retrieval later.
/// </summary>
/// <param name="newValue">The object being stored.</param>
/// <param name="container">The container this value belongs to.</param>
/// <remarks>Setting a value will attempt to release the lock acquired by
/// <see cref="SynchronizedLifetimeManager.GetValue"/>.</remarks>
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
_value = newValue;
if (_value is IDisposable) _container.Add(new DisposableAction(() => RemoveValue(_container)));
TryExit();
}

#endregion


#region IRequiresRecovery

/// <summary>
/// A method that does whatever is needed to clean up
/// as part of cleaning up after an exception.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public void Recover()
{
TryExit();
}

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
{
Monitor.Exit(_lockObj);
}
catch (SynchronizationLockException)
{
// Noop here - we don't hold the lock and that's ok.
}
}

#endregion


#region Nested Types

private class DisposableAction : IDisposable
Expand All @@ -48,6 +136,5 @@ public void Dispose()
}

#endregion

}
}
2 changes: 1 addition & 1 deletion tests/UnityDependencyInjectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void Disposes_InReverseOrderOfCreation()

// Assert
Assert.Equal(outer, callback.Disposed[0]);
Assert.Equal(multipleServices.Reverse(), callback.Disposed.Skip(1).Take(3).OfType<IFakeMultipleService>());
Assert.Equal(outer.MultipleServices.Reverse(), callback.Disposed.Skip(1).Take(3).OfType<IFakeMultipleService>());
Assert.Equal(outer.SingleService, callback.Disposed[4]);

}
Expand Down

0 comments on commit 38f340b

Please sign in to comment.