Files
Umbraco-CMS/src/Umbraco.Core/Scoping/ScopeProvider.cs

611 lines
24 KiB
C#
Raw Normal View History

2017-01-12 18:28:25 +01:00
using System;
2017-02-13 09:04:19 +01:00
using System.Collections;
using System.Collections.Generic;
using System.Data;
2017-01-12 18:28:25 +01:00
using System.Runtime.Remoting.Messaging;
2017-02-23 19:00:26 +01:00
using System.Text;
2017-01-12 18:28:25 +01:00
using System.Web;
using Umbraco.Core.Events;
2017-01-12 18:28:25 +01:00
using Umbraco.Core.Persistence;
2017-02-13 09:04:19 +01:00
#if DEBUG_SCOPES
using System.Linq;
#endif
2017-01-12 18:28:25 +01:00
namespace Umbraco.Core.Scoping
{
/// <summary>
/// Implements <see cref="IScopeProvider"/>.
/// </summary>
2017-01-12 19:32:44 +01:00
internal class ScopeProvider : IScopeProviderInternal
2017-01-12 18:28:25 +01:00
{
public ScopeProvider(IDatabaseFactory2 databaseFactory)
{
DatabaseFactory = databaseFactory;
}
static ScopeProvider()
{
SafeCallContext.Register(
() =>
{
2017-02-13 09:04:19 +01:00
var scope = GetCallContextObject<IScopeInternal>(ScopeItemKey);
var context = GetCallContextObject<ScopeContext>(ContextItemKey);
SetCallContextObject(ScopeItemKey, null);
SetCallContextObject(ContextItemKey, null);
2017-02-03 20:01:43 +01:00
return Tuple.Create(scope, context);
2017-01-12 18:28:25 +01:00
},
2017-02-03 20:01:43 +01:00
o =>
2017-01-12 18:28:25 +01:00
{
2017-02-03 20:01:43 +01:00
// cannot re-attached over leaked scope/context
2017-02-04 15:44:56 +01:00
// except of course over NoScope (which leaks)
2017-02-14 09:09:05 +01:00
var ambientScope = GetCallContextObject<IScope>(ScopeItemKey);
2017-02-04 15:44:56 +01:00
if (ambientScope != null)
{
var ambientNoScope = ambientScope as NoScope;
if (ambientNoScope == null)
throw new Exception("Found leaked scope when restoring call context.");
// this should rollback any pending transaction
ambientNoScope.Dispose();
}
2017-02-14 09:09:05 +01:00
if (GetCallContextObject<ScopeContext>(ContextItemKey) != null)
throw new Exception("Found leaked context when restoring call context.");
2017-02-03 20:01:43 +01:00
2017-02-13 09:04:19 +01:00
var t = (Tuple<IScopeInternal, ScopeContext>) o;
SetCallContextObject(ScopeItemKey, t.Item1);
SetCallContextObject(ContextItemKey, t.Item2);
2017-01-12 18:28:25 +01:00
});
}
public IDatabaseFactory2 DatabaseFactory { get; private set; }
2017-02-13 09:04:19 +01:00
#region Context
// objects that go into the logical call context better be serializable else they'll eventually
// cause issues whenever some cross-AppDomain code executes - could be due to ReSharper running
// tests, any other things (see https://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx),
// but we don't want to make all of our objects serializable since they are *not* meant to be
// used in cross-AppDomain scenario anyways.
// in addition, whatever goes into the logical call context is serialized back and forth any
// time cross-AppDomain code executes, so if we put an "object" there, we'll can *another*
// "object" instance - and so we cannot use a random object as a key.
// so what we do is: we register a guid in the call context, and we keep a table mapping those
// guids to the actual objects. the guid serializes back and forth without causing any issue,
// and we can retrieve the actual objects from the table.
// only issue: how are we supposed to clear the table? we can't, really. objects should take
// care of de-registering themselves from context.
// everything we use does, except the NoScope scope, which just stays there
//
// during tests, NoScope can to into call context... nothing much we can do about it
2017-02-13 09:04:19 +01:00
private static readonly object StaticCallContextObjectsLock = new object();
private static readonly Dictionary<Guid, object> StaticCallContextObjects
= new Dictionary<Guid, object>();
2017-02-13 09:04:19 +01:00
#if DEBUG_SCOPES
public Dictionary<Guid, object> CallContextObjects
{
2017-02-13 09:04:19 +01:00
get
{
2017-02-13 09:04:19 +01:00
lock (StaticCallContextObjectsLock)
{
// capture in a dictionary
return StaticCallContextObjects.ToDictionary(x => x.Key, x => x.Value);
}
}
}
2017-02-13 09:04:19 +01:00
#endif
2017-02-13 09:04:19 +01:00
private static T GetCallContextObject<T>(string key)
where T : class
{
2017-02-13 09:04:19 +01:00
var objectKey = CallContext.LogicalGetData(key).AsGuid();
2017-02-23 19:00:26 +01:00
if (objectKey == Guid.Empty) return null;
2017-02-13 09:04:19 +01:00
lock (StaticCallContextObjectsLock)
{
2017-02-13 09:04:19 +01:00
object callContextObject;
2017-02-14 09:09:05 +01:00
if (StaticCallContextObjects.TryGetValue(objectKey, out callContextObject))
{
#if DEBUG_SCOPES
2017-02-23 19:00:26 +01:00
Logging.LogHelper.Debug<ScopeProvider>("Got " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8));
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
2017-02-14 09:09:05 +01:00
#endif
return (T) callContextObject;
}
2017-02-23 19:00:26 +01:00
Logging.LogHelper.Warn<ScopeProvider>("Missed " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8));
2017-02-14 09:09:05 +01:00
#if DEBUG_SCOPES
2017-02-23 19:00:26 +01:00
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
2017-02-14 09:09:05 +01:00
#endif
return null;
}
}
2017-02-14 09:09:05 +01:00
private static void SetCallContextObject(string key, IInstanceIdentifiable value)
2017-02-13 09:04:19 +01:00
{
#if DEBUG_SCOPES
// manage the 'context' that contains the scope (null, "http" or "call")
2017-02-14 09:09:05 +01:00
// only for scopes of course!
if (key == ScopeItemKey)
2017-02-13 09:04:19 +01:00
{
2017-02-14 09:09:05 +01:00
// first, null-register the existing value
var ambientKey = CallContext.LogicalGetData(ScopeItemKey).AsGuid();
object o = null;
lock (StaticCallContextObjectsLock)
{
if (ambientKey != default(Guid))
StaticCallContextObjects.TryGetValue(ambientKey, out o);
}
var ambientScope = o as IScope;
if (ambientScope != null) RegisterContext(ambientScope, null);
// then register the new value
var scope = value as IScope;
if (scope != null) RegisterContext(scope, "call");
2017-02-13 09:04:19 +01:00
}
#endif
if (value == null)
{
var objectKey = CallContext.LogicalGetData(key).AsGuid();
CallContext.FreeNamedDataSlot(key);
if (objectKey == default (Guid)) return;
lock (StaticCallContextObjectsLock)
{
2017-02-14 09:09:05 +01:00
#if DEBUG_SCOPES
2017-02-23 19:00:26 +01:00
Logging.LogHelper.Debug<ScopeProvider>("Remove Object " + objectKey.ToString("N").Substring(0, 8));
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
2017-02-14 09:09:05 +01:00
#endif
2017-02-13 09:04:19 +01:00
StaticCallContextObjects.Remove(objectKey);
}
}
else
{
// note - we are *not* detecting an already-existing value
// because our code in this class *always* sets to null before
// setting to a real value
2017-02-14 09:09:05 +01:00
var objectKey = value.InstanceId;
2017-02-13 09:04:19 +01:00
lock (StaticCallContextObjectsLock)
{
2017-02-14 09:09:05 +01:00
#if DEBUG_SCOPES
2017-02-23 19:00:26 +01:00
Logging.LogHelper.Debug<ScopeProvider>("AddObject " + objectKey.ToString("N").Substring(0, 8));
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
2017-02-14 09:09:05 +01:00
#endif
2017-02-13 09:04:19 +01:00
StaticCallContextObjects.Add(objectKey, value);
}
CallContext.LogicalSetData(key, objectKey);
}
}
// this is for tests exclusively until we have a proper accessor in v8
2017-02-13 09:04:19 +01:00
internal static Func<IDictionary> HttpContextItemsGetter { get; set; }
private static IDictionary HttpContextItems
{
get
{
return HttpContextItemsGetter == null
? (HttpContext.Current == null ? null : HttpContext.Current.Items)
: HttpContextItemsGetter();
}
}
public static T GetHttpContextObject<T>(string key, bool required = true)
where T : class
{
var httpContextItems = HttpContextItems;
if (httpContextItems != null)
return (T)httpContextItems[key];
if (required)
throw new Exception("HttpContext.Current is null.");
return null;
}
private static bool SetHttpContextObject(string key, object value, bool required = true)
{
var httpContextItems = HttpContextItems;
if (httpContextItems == null)
{
if (required)
throw new Exception("HttpContext.Current is null.");
return false;
}
#if DEBUG_SCOPES
// manage the 'context' that contains the scope (null, "http" or "call")
2017-02-14 09:09:05 +01:00
// only for scopes of course!
if (key == ScopeItemKey)
{
// first, null-register the existing value
var ambientScope = (IScope)httpContextItems[ScopeItemKey];
if (ambientScope != null) RegisterContext(ambientScope, null);
// then register the new value
var scope = value as IScope;
if (scope != null) RegisterContext(scope, "http");
}
2017-02-13 09:04:19 +01:00
#endif
if (value == null)
httpContextItems.Remove(key);
else
httpContextItems[key] = value;
return true;
}
2017-02-14 09:09:05 +01:00
#endregion
2017-02-13 09:04:19 +01:00
#region Ambient Context
internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext";
internal static ScopeContext AmbientContextInternal
{
get
{
// try http context, fallback onto call context
2017-02-13 09:04:19 +01:00
var value = GetHttpContextObject<ScopeContext>(ContextItemKey, false);
return value ?? GetCallContextObject<ScopeContext>(ContextItemKey);
}
set
{
// clear both
2017-02-13 09:04:19 +01:00
SetHttpContextObject(ContextItemKey, null, false);
SetCallContextObject(ContextItemKey, null);
if (value == null) return;
// set http/call context
if (SetHttpContextObject(ContextItemKey, value, false) == false)
SetCallContextObject(ContextItemKey, value);
}
}
/// <inheritdoc />
public ScopeContext AmbientContext
{
2017-02-13 09:04:19 +01:00
get { return AmbientContextInternal; }
}
#endregion
#region Ambient Scope
internal const string ScopeItemKey = "Umbraco.Core.Scoping.Scope";
internal const string ScopeRefItemKey = "Umbraco.Core.Scoping.ScopeReference";
// only 1 instance which can be disposed and disposed again
private static readonly ScopeReference StaticScopeReference = new ScopeReference(new ScopeProvider(null));
2017-01-12 18:28:25 +01:00
2017-02-13 09:04:19 +01:00
internal static IScopeInternal AmbientScopeInternal
2017-01-12 18:28:25 +01:00
{
get
{
// try http context, fallback onto call context
2017-02-13 09:04:19 +01:00
var value = GetHttpContextObject<IScopeInternal>(ScopeItemKey, false);
return value ?? GetCallContextObject<IScopeInternal>(ScopeItemKey);
}
2017-01-12 18:28:25 +01:00
set
{
// clear both
2017-02-13 09:04:19 +01:00
SetHttpContextObject(ScopeItemKey, null, false);
SetHttpContextObject(ScopeRefItemKey, null, false);
SetCallContextObject(ScopeItemKey, null);
if (value == null) return;
// set http/call context
if (value.CallContext == false && SetHttpContextObject(ScopeItemKey, value, false))
SetHttpContextObject(ScopeRefItemKey, StaticScopeReference);
else
SetCallContextObject(ScopeItemKey, value);
2017-01-12 18:28:25 +01:00
}
}
2017-01-16 17:41:46 +01:00
/// <inheritdoc />
2017-02-03 20:01:43 +01:00
public IScopeInternal AmbientScope
2017-01-12 18:28:25 +01:00
{
2017-02-13 09:04:19 +01:00
get { return AmbientScopeInternal; }
internal set { AmbientScopeInternal = value; }
2017-01-12 18:28:25 +01:00
}
2017-01-16 17:41:46 +01:00
/// <inheritdoc />
2017-02-03 20:01:43 +01:00
public IScopeInternal GetAmbientOrNoScope()
2017-01-16 17:41:46 +01:00
{
2017-02-13 09:04:19 +01:00
return AmbientScope ?? (AmbientScope = new NoScope(this));
2017-01-16 17:41:46 +01:00
}
2017-01-12 18:28:25 +01:00
#endregion
public void SetAmbient(IScopeInternal scope, ScopeContext context = null)
{
2017-02-13 09:04:19 +01:00
// clear all
SetHttpContextObject(ScopeItemKey, null, false);
SetHttpContextObject(ScopeRefItemKey, null, false);
SetCallContextObject(ScopeItemKey, null);
SetHttpContextObject(ContextItemKey, null, false);
SetCallContextObject(ContextItemKey, null);
if (scope == null)
{
2017-02-13 09:04:19 +01:00
if (context != null)
throw new ArgumentException("Must be null if scope is null.", "context");
return;
}
2017-02-13 09:04:19 +01:00
if (scope.CallContext == false && SetHttpContextObject(ScopeItemKey, scope, false))
{
SetHttpContextObject(ScopeRefItemKey, StaticScopeReference);
SetHttpContextObject(ContextItemKey, context);
}
else
{
2017-02-13 09:04:19 +01:00
SetCallContextObject(ScopeItemKey, scope);
SetCallContextObject(ContextItemKey, context);
}
}
2017-01-16 17:41:46 +01:00
/// <inheritdoc />
public IScope CreateDetachedScope(
2017-02-02 09:49:23 +01:00
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
2017-02-11 11:54:22 +01:00
IEventDispatcher eventDispatcher = null,
2017-02-02 09:49:23 +01:00
bool? scopeFileSystems = null)
2017-01-12 18:28:25 +01:00
{
2017-02-11 11:54:22 +01:00
return new Scope(this, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
2017-01-12 18:28:25 +01:00
}
2017-01-16 17:41:46 +01:00
/// <inheritdoc />
public void AttachScope(IScope other, bool callContext = false)
2017-01-12 18:28:25 +01:00
{
var otherScope = other as Scope;
if (otherScope == null)
2017-01-17 16:46:34 +01:00
throw new ArgumentException("Not a Scope instance.");
2017-01-12 18:28:25 +01:00
if (otherScope.Detachable == false)
2017-01-17 16:46:34 +01:00
throw new ArgumentException("Not a detachable scope.");
2017-01-12 18:28:25 +01:00
2017-02-14 09:09:05 +01:00
if (otherScope.Attached)
throw new InvalidOperationException("Already attached.");
otherScope.Attached = true;
2017-01-17 16:46:34 +01:00
otherScope.OrigScope = AmbientScope;
2017-02-03 20:01:43 +01:00
otherScope.OrigContext = AmbientContext;
otherScope.CallContext = callContext;
SetAmbient(otherScope, otherScope.Context);
2017-01-12 18:28:25 +01:00
}
2017-01-16 17:41:46 +01:00
/// <inheritdoc />
2017-01-12 18:28:25 +01:00
public IScope DetachScope()
{
var ambient = AmbientScope;
if (ambient == null)
2017-01-17 16:46:34 +01:00
throw new InvalidOperationException("There is no ambient scope.");
2017-01-12 18:28:25 +01:00
var noScope = ambient as NoScope;
if (noScope != null)
2017-01-17 16:46:34 +01:00
throw new InvalidOperationException("Cannot detach NoScope.");
2017-01-12 18:28:25 +01:00
var scope = ambient as Scope;
if (scope == null)
2017-01-17 16:46:34 +01:00
throw new Exception("Ambient scope is not a Scope instance.");
2017-01-12 18:28:25 +01:00
if (scope.Detachable == false)
2017-01-17 16:46:34 +01:00
throw new InvalidOperationException("Ambient scope is not detachable.");
2017-01-12 18:28:25 +01:00
SetAmbient(scope.OrigScope, scope.OrigContext);
2017-01-12 18:28:25 +01:00
scope.OrigScope = null;
2017-02-03 20:01:43 +01:00
scope.OrigContext = null;
2017-02-14 09:09:05 +01:00
scope.Attached = false;
2017-01-12 18:28:25 +01:00
return scope;
}
2017-01-16 17:41:46 +01:00
/// <inheritdoc />
public IScope CreateScope(
2017-02-02 09:49:23 +01:00
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
2017-02-11 11:54:22 +01:00
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null,
bool callContext = false)
2017-01-12 18:28:25 +01:00
{
var ambient = AmbientScope;
if (ambient == null)
{
var ambientContext = AmbientContext;
var newContext = ambientContext == null ? new ScopeContext() : null;
2017-02-11 11:54:22 +01:00
var scope = new Scope(this, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
// assign only if scope creation did not throw!
SetAmbient(scope, newContext ?? ambientContext);
return scope;
}
2017-01-12 18:28:25 +01:00
2017-01-16 17:41:46 +01:00
// replace noScope with a real one
2017-01-12 18:28:25 +01:00
var noScope = ambient as NoScope;
if (noScope != null)
{
2017-01-18 10:36:11 +01:00
#if DEBUG_SCOPES
Disposed(noScope);
#endif
2017-01-12 18:28:25 +01:00
// peta poco nulls the shared connection after each command unless there's a trx
2017-01-16 17:41:46 +01:00
var database = noScope.DatabaseOrNull;
if (database != null && database.InTransaction)
2017-01-17 16:46:34 +01:00
throw new Exception("NoScope is in a transaction.");
var ambientContext = AmbientContext;
var newContext = ambientContext == null ? new ScopeContext() : null;
2017-02-11 11:54:22 +01:00
var scope = new Scope(this, noScope, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
// assign only if scope creation did not throw!
SetAmbient(scope, newContext ?? ambientContext);
return scope;
2017-01-12 18:28:25 +01:00
}
2017-02-03 20:01:43 +01:00
var ambientScope = ambient as Scope;
if (ambientScope == null) throw new Exception("Ambient scope is not a Scope instance.");
2017-01-12 18:28:25 +01:00
2017-02-11 11:54:22 +01:00
var nested = new Scope(this, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
SetAmbient(nested, AmbientContext);
return nested;
}
/// <inheritdoc />
public void Reset()
2017-01-12 18:28:25 +01:00
{
var scope = AmbientScope as Scope;
if (scope != null)
scope.Reset();
2017-01-16 17:41:46 +01:00
StaticScopeReference.Dispose();
2017-01-12 18:28:25 +01:00
}
2017-01-17 16:46:34 +01:00
/// <inheritdoc />
public ScopeContext Context
{
get { return AmbientContext; }
}
2017-01-17 16:46:34 +01:00
#if DEBUG_SCOPES
// this code needs TLC
2017-02-06 10:08:29 +01:00
//
// the idea here is to keep in a list all the scopes that have been created, and to remove them
// when they are disposed, so we can track leaks, ie scopes that would not be properly taken
// care of by our code
//
// note: the code could probably be optimized... but this is NOT supposed to go into any real
// live build, either production or debug - it's just a debugging tool for the time being
2017-01-17 16:46:34 +01:00
// helps identifying when non-httpContext scopes are created by logging the stack trace
2017-01-18 10:36:11 +01:00
//private void LogCallContextStack()
//{
// var trace = Environment.StackTrace;
// if (trace.IndexOf("ScheduledPublishing") > 0)
// LogHelper.Debug<ScopeProvider>("CallContext: Scheduled Publishing");
// else if (trace.IndexOf("TouchServerTask") > 0)
// LogHelper.Debug<ScopeProvider>("CallContext: Server Registration");
// else if (trace.IndexOf("LogScrubber") > 0)
// LogHelper.Debug<ScopeProvider>("CallContext: Log Scrubber");
// else
// LogHelper.Debug<ScopeProvider>("CallContext: " + Environment.StackTrace);
//}
2017-02-06 10:08:29 +01:00
// all scope instances that are currently beeing tracked
private static readonly object StaticScopeInfosLock = new object();
2017-02-14 09:09:05 +01:00
private static readonly Dictionary<IScope, ScopeInfo> StaticScopeInfos = new Dictionary<IScope, ScopeInfo>();
2017-01-18 10:36:11 +01:00
2017-02-06 10:08:29 +01:00
public IEnumerable<ScopeInfo> ScopeInfos
{
get
{
lock (StaticScopeInfosLock)
{
2017-02-14 09:09:05 +01:00
return StaticScopeInfos.Values.ToArray(); // capture in an array
2017-02-06 10:08:29 +01:00
}
}
}
2017-01-18 10:36:11 +01:00
2017-02-13 09:04:19 +01:00
public ScopeInfo GetScopeInfo(IScope scope)
{
lock (StaticScopeInfosLock)
{
2017-02-14 09:09:05 +01:00
ScopeInfo scopeInfo;
return StaticScopeInfos.TryGetValue(scope, out scopeInfo) ? scopeInfo : null;
2017-02-13 09:04:19 +01:00
}
}
2017-01-18 10:36:11 +01:00
//private static void Log(string message, UmbracoDatabase database)
//{
// LogHelper.Debug<ScopeProvider>(message + " (" + (database == null ? "" : database.InstanceSid) + ").");
//}
2017-02-13 09:04:19 +01:00
// register a scope and capture its ctor stacktrace
2017-02-06 10:08:29 +01:00
public void RegisterScope(IScope scope)
2017-01-17 16:46:34 +01:00
{
2017-02-06 10:08:29 +01:00
lock (StaticScopeInfosLock)
{
2017-02-14 09:09:05 +01:00
if (StaticScopeInfos.ContainsKey(scope)) throw new Exception("oops: already registered.");
2017-02-23 19:00:26 +01:00
Logging.LogHelper.Debug<ScopeProvider>("Register " + scope.InstanceId.ToString("N").Substring(0, 8));
2017-02-14 09:09:05 +01:00
StaticScopeInfos[scope] = new ScopeInfo(scope, Environment.StackTrace);
2017-02-06 10:08:29 +01:00
}
2017-01-17 16:46:34 +01:00
}
2017-02-13 09:04:19 +01:00
// register that a scope is in a 'context'
// 'context' that contains the scope (null, "http" or "call")
2017-01-18 10:36:11 +01:00
public static void RegisterContext(IScope scope, string context)
{
2017-02-06 10:08:29 +01:00
lock (StaticScopeInfosLock)
2017-01-18 10:36:11 +01:00
{
2017-02-14 09:09:05 +01:00
ScopeInfo info;
if (StaticScopeInfos.TryGetValue(scope, out info) == false) info = null;
2017-02-06 10:08:29 +01:00
if (info == null)
{
if (context == null) return;
throw new Exception("oops: unregistered scope.");
}
2017-02-23 19:00:26 +01:00
var sb = new StringBuilder();
var s = scope;
while (s != null)
{
if (sb.Length > 0) sb.Append(" < ");
sb.Append(s.InstanceId.ToString("N").Substring(0, 8));
var ss = s as IScopeInternal;
s = ss == null ? null : ss.ParentScope;
}
Logging.LogHelper.Debug<ScopeProvider>("Register " + (context ?? "null") + " context " + sb);
2017-02-06 10:08:29 +01:00
if (context == null) info.NullStack = Environment.StackTrace;
2017-02-23 19:00:26 +01:00
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 16));
2017-02-06 10:08:29 +01:00
info.Context = context;
2017-01-18 10:36:11 +01:00
}
}
2017-01-17 16:46:34 +01:00
2017-02-23 19:00:26 +01:00
private static string Head(string s, int count)
{
var pos = 0;
var i = 0;
while (i < count && pos >= 0)
{
pos = s.IndexOf("\r\n", pos + 1, StringComparison.OrdinalIgnoreCase);
i++;
}
if (pos < 0) return s;
return s.Substring(0, pos);
}
2017-01-18 10:36:11 +01:00
public void Disposed(IScope scope)
2017-01-17 16:46:34 +01:00
{
2017-02-06 10:08:29 +01:00
lock (StaticScopeInfosLock)
2017-01-18 10:36:11 +01:00
{
2017-02-14 09:09:05 +01:00
if (StaticScopeInfos.ContainsKey(scope))
2017-02-06 10:08:29 +01:00
{
// enable this by default
2017-02-14 09:09:05 +01:00
//Console.WriteLine("unregister " + scope.InstanceId.ToString("N").Substring(0, 8));
StaticScopeInfos.Remove(scope);
2017-02-23 19:00:26 +01:00
Logging.LogHelper.Debug<ScopeProvider>("Remove " + scope.InstanceId.ToString("N").Substring(0, 8));
2017-01-18 10:36:11 +01:00
2017-02-06 10:08:29 +01:00
// instead, enable this to keep *all* scopes
// beware, there can be a lot of scopes!
//info.Disposed = true;
//info.DisposedStack = Environment.StackTrace;
}
2017-01-18 10:36:11 +01:00
}
2017-01-17 16:46:34 +01:00
}
#endif
2017-01-18 10:36:11 +01:00
}
2017-01-17 16:46:34 +01:00
2017-01-18 10:36:11 +01:00
#if DEBUG_SCOPES
public class ScopeInfo
{
public ScopeInfo(IScope scope, string ctorStack)
{
Scope = scope;
Created = DateTime.Now;
CtorStack = ctorStack;
}
2017-02-06 10:08:29 +01:00
public IScope Scope { get; private set; } // the scope itself
// the scope's parent identifier
2017-01-18 10:36:11 +01:00
public Guid Parent { get { return (Scope is NoScope || ((Scope) Scope).ParentScope == null) ? Guid.Empty : ((Scope) Scope).ParentScope.InstanceId; } }
2017-02-06 10:08:29 +01:00
public DateTime Created { get; private set; } // the date time the scope was created
public bool Disposed { get; set; } // whether the scope has been disposed already
public string Context { get; set; } // the current 'context' that contains the scope (null, "http" or "lcc")
2017-01-18 10:36:11 +01:00
public string CtorStack { get; private set; } // the stacktrace of the scope ctor
public string DisposedStack { get; set; } // the stacktrace when disposed
2017-02-06 10:08:29 +01:00
public string NullStack { get; set; } // the stacktrace when the 'context' that contains the scope went null
2017-01-12 18:28:25 +01:00
}
2017-01-18 10:36:11 +01:00
#endif
2017-01-12 18:28:25 +01:00
}