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.Security;
using System.Threading;
namespace Castle.MicroKernel.Lifestyle.Scoped
{
public class CallContextLifetimeScope : ILifetimeScope, IDisposable
{
private static readonly ConcurrentDictionary<Guid, CallContextLifetimeScope> allScopes = new ConcurrentDictionary<Guid, CallContextLifetimeScope>();
private static readonly AsyncLocal<Guid> asyncLocal = new AsyncLocal<Guid>();
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);
} 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)
{
asyncLocal.Value = lifetimeScope.contextId;
}
[SecuritySafeCritical]
public static CallContextLifetimeScope ObtainCurrentScope()
{
Guid value = asyncLocal.Value;
allScopes.TryGetValue(value, out CallContextLifetimeScope value2);
return value2;
}
}
}