CallContextLifetimeScope
Provides explicit lifetime scoping within logical path of execution. Used for types with Scoped.
using Castle.Core;
using Castle.Core.Internal;
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.Runtime.Remoting.Messaging;
using System.Security;
namespace Castle.MicroKernel.Lifestyle.Scoped
{
public class CallContextLifetimeScope : ILifetimeScope, IDisposable
{
private static readonly ConcurrentDictionary<Guid, CallContextLifetimeScope> allScopes = new ConcurrentDictionary<Guid, CallContextLifetimeScope>();
private static readonly string callContextKey = "castle.lifetime-scope-" + AppDomain.CurrentDomain.Id.ToString(CultureInfo.InvariantCulture);
private readonly Guid contextId = Guid.NewGuid();
private readonly CallContextLifetimeScope parentScope;
private readonly Lock lock = Lock.Create();
private ScopeCache cache = new ScopeCache();
public CallContextLifetimeScope()
{
contextId = Guid.NewGuid();
parentScope = ObtainCurrentScope();
allScopes.TryAdd(contextId, this);
SetCurrentScope(this);
}
[SecuritySafeCritical]
public void Dispose()
{
IUpgradeableLockHolder val = lock.ForReadingUpgradeable();
try {
if (cache == null)
return;
val.Upgrade();
cache.Dispose();
cache = null;
if (parentScope != null)
SetCurrentScope(parentScope);
else
CallContext.FreeNamedDataSlot(callContextKey);
} finally {
((IDisposable)val)?.Dispose();
}
allScopes.TryRemove(contextId, out CallContextLifetimeScope _);
}
public Burden GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
{
IUpgradeableLockHolder val = lock.ForReadingUpgradeable();
try {
Burden burden = cache[model];
if (burden == null) {
val.Upgrade();
burden = createInstance(delegate {
});
cache[model] = burden;
}
return burden;
} finally {
((IDisposable)val)?.Dispose();
}
}
[SecuritySafeCritical]
private static void SetCurrentScope(CallContextLifetimeScope lifetimeScope)
{
CallContext.LogicalSetData(callContextKey, (object)lifetimeScope.contextId);
}
[SecuritySafeCritical]
public static CallContextLifetimeScope ObtainCurrentScope()
{
object obj = CallContext.LogicalGetData(callContextKey);
if (obj == null)
return null;
allScopes.TryGetValue((Guid)obj, out CallContextLifetimeScope value);
return value;
}
}
}