CallContextLifetimeScope
Provides explicit lifetime scoping within logical path of execution. Used for types with Scoped.
using Castle.Core;
using Castle.Core.Internal;
using Castle.Windsor;
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> appDomainLocalInstanceCache = new ConcurrentDictionary<Guid, CallContextLifetimeScope>();
private static readonly string keyInCallContext = "castle.lifetime-scope-" + AppDomain.CurrentDomain.Id.ToString(CultureInfo.InvariantCulture);
private readonly Guid contextId;
private readonly Lock lock = Lock.Create();
private readonly CallContextLifetimeScope parentScope;
private ScopeCache cache = new ScopeCache();
public CallContextLifetimeScope(IKernel container)
: this()
{
}
public CallContextLifetimeScope()
{
CallContextLifetimeScope callContextLifetimeScope = ObtainCurrentScope();
if (callContextLifetimeScope != null)
parentScope = callContextLifetimeScope;
contextId = Guid.NewGuid();
appDomainLocalInstanceCache.TryAdd(contextId, this);
SetCurrentScope(this);
}
public CallContextLifetimeScope(IWindsorContainer container)
: 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(keyInCallContext);
} finally {
((IDisposable)val)?.Dispose();
}
appDomainLocalInstanceCache.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 void SetCurrentScope(CallContextLifetimeScope lifetimeScope)
{
CallContext.LogicalSetData(keyInCallContext, (object)lifetimeScope.contextId);
}
[SecuritySafeCritical]
public static CallContextLifetimeScope ObtainCurrentScope()
{
object obj = CallContext.LogicalGetData(keyInCallContext);
if (obj == null)
return null;
appDomainLocalInstanceCache.TryGetValue((Guid)obj, out CallContextLifetimeScope value);
return value;
}
}
}