diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs
index f8f31ee695..27013d8ecd 100644
--- a/src/Umbraco.Core/DatabaseContext.cs
+++ b/src/Umbraco.Core/DatabaseContext.cs
@@ -94,13 +94,13 @@ namespace Umbraco.Core
public ISqlSyntaxProvider SqlSyntax { get; private set; }
///
- /// Gets the object for doing CRUD operations against custom tables that resides in the Umbraco database.
+ /// Gets an "ambient" database for doing CRUD operations against custom tables that resides in the Umbraco database.
///
///
/// Should not be used for operation against standard Umbraco tables; as services should be used instead.
/// Gets or creates an "ambient" database that is either stored in http context + available for the whole
- /// request + auto-disposed at the end of the request, or in call context if there is no http context - in which
- /// case it needs to be explicitely disposed so it is removed from call context.
+ /// request + auto-disposed at the end of the request, or stored in call context if there is no http context - in which
+ /// case it *must* be explicitely disposed (which will remove it from call context).
///
public virtual UmbracoDatabase Database
{
@@ -108,24 +108,32 @@ namespace Umbraco.Core
}
///
- /// Replaces the "ambient" database (if any) and installs a new object.
+ /// Replaces the "ambient" database (if any) and by a new temp database.
///
///
- /// The returned IDisposable *must* be diposed in order to properly disposed the safe database and
+ /// The returned IDisposable *must* be diposed in order to properly dispose the temp database and
/// restore the original "ambient" database (if any).
/// This is to be used in background tasks to ensure that they have an "ambient" database which
/// will be properly removed from call context and does not interfere with anything else. In most case
/// it is not replacing anything, just temporarily installing a database in context.
///
- public virtual IDisposable UsingSafeDatabase
+ public virtual IDisposable UseSafeDatabase(bool force = false)
{
- get
+ var factory = _factory as DefaultDatabaseFactory;
+ if (factory == null) throw new NotSupportedException();
+ if (DefaultDatabaseFactory.HasAmbientDatabase)
{
- var factory = _factory as DefaultDatabaseFactory;
- if (factory == null) throw new NotSupportedException();
- var database = factory.CreateDatabaseInstance(DefaultDatabaseFactory.ContextOwner.None);
- return new UsedDatabase(database, _logger);
+ // has ambient,
+ // if forcing, detach it and replace it with a new, temp, database
+ // when the UsingDatabase is disposed, the temp database is disposed and the original one is re-instated
+ // else do nothing (and nothing will be disposed)
+ return force
+ ? new UsingDatabase(DefaultDatabaseFactory.DetachAmbientDatabase(), factory.CreateDatabase())
+ : new UsingDatabase(null, null);
}
+
+ // create a new, temp, database (will be disposed with UsingDatabase)
+ return new UsingDatabase(null, factory.CreateDatabase());
}
///
@@ -788,30 +796,26 @@ namespace Umbraco.Core
return true;
}
- private class UsedDatabase : IDisposable
+ private class UsingDatabase : IDisposable
{
- private readonly UmbracoDatabase _database;
- private readonly ILogger _logger;
+ private readonly UmbracoDatabase _orig;
+ private readonly UmbracoDatabase _temp;
- public UsedDatabase(UmbracoDatabase database, ILogger logger)
+ public UsingDatabase(UmbracoDatabase orig, UmbracoDatabase temp)
{
- _logger = logger;
-
- // replace with ours
- _database = DefaultDatabaseFactory.DetachDatabase();
- _logger.Debug("Detached db " + (_database == null ? "x" : _database.InstanceSid) + ".");
- DefaultDatabaseFactory.AttachDatabase(database);
- _logger.Debug("Attached db " + database.InstanceSid + ".");
+ _orig = orig;
+ _temp = temp;
}
public void Dispose()
{
- // restore the original DB
- var db = DefaultDatabaseFactory.DetachDatabase();
- _logger.Debug("Detached db " + db.InstanceSid + ".");
- db.Dispose();
- DefaultDatabaseFactory.AttachDatabase(_database);
- _logger.Debug("Attached db " + (_database == null ? "x" : _database.InstanceSid) + ".");
+ if (_temp != null)
+ {
+ _temp.Dispose();
+ if (_orig != null)
+ DefaultDatabaseFactory.AttachAmbientDatabase(_orig);
+ }
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs
index 5139da20d8..2ffee29490 100644
--- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs
+++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using System.Web;
using Umbraco.Core.Logging;
@@ -250,12 +251,23 @@ namespace Umbraco.Core.Persistence
static DefaultDatabaseFactory()
{
- SafeCallContext.Register(DetachDatabase, AttachDatabase);
+ SafeCallContext.Register(DetachAmbientDatabase, AttachAmbientDatabase);
}
+ // gets a value indicating whether there is an ambient database
+ internal static bool HasAmbientDatabase
+ {
+ get
+ {
+ return HttpContext.Current == null
+ ? NonContextValue != null
+ : HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] != null;
+ }
+ }
+
// detaches the current database
// ie returns the database and remove it from whatever is "context"
- internal static UmbracoDatabase DetachDatabase()
+ internal static UmbracoDatabase DetachAmbientDatabase()
{
UmbracoDatabase database;
@@ -277,7 +289,7 @@ namespace Umbraco.Core.Persistence
// attach a current database
// ie assign it to whatever is "context"
// throws if there already is a database
- internal static void AttachDatabase(object o)
+ internal static void AttachAmbientDatabase(object o)
{
var database = o as UmbracoDatabase;
if (o != null && database == null) throw new ArgumentException("Not an UmbracoDatabase.", "o");
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 6da7f4519b..24d889a06f 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -21,7 +21,7 @@
full
false
bin\Debug\
- DEBUG;TRACE
+ TRACE;DEBUG;DEBUG_DATABASES
prompt
4
false
diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Web/Scheduling/LogScrubber.cs
index da49404b4d..7737101961 100644
--- a/src/Umbraco.Web/Scheduling/LogScrubber.cs
+++ b/src/Umbraco.Web/Scheduling/LogScrubber.cs
@@ -78,7 +78,7 @@ namespace Umbraco.Web.Scheduling
}
// running on a background task, requires a safe database (see UsingSafeDatabase doc)
- using (ApplicationContext.Current.DatabaseContext.UsingSafeDatabase)
+ using (ApplicationContext.Current.DatabaseContext.UseSafeDatabase(true))
using (DisposableTimer.DebugDuration("Log scrubbing executing", "Log scrubbing complete"))
{
Log.CleanLogs(GetLogScrubbingMaximumAge(_settings));
diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs
index dc6c27959d..ba4fdddba5 100644
--- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs
+++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs
@@ -83,10 +83,8 @@ namespace Umbraco.Web.Scheduling
};
// running on a background task, requires a safe database (see UsingSafeDatabase doc)
- //
- // this is because GetAuthenticationHeaderValue uses UserService to load the current user, hence requires a database
- //
- using (ApplicationContext.Current.DatabaseContext.UsingSafeDatabase)
+ // (GetAuthenticationHeaderValue uses UserService to load the current user, hence requires a database)
+ using (ApplicationContext.Current.DatabaseContext.UseSafeDatabase(true))
{
//pass custom the authorization header
request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext);
diff --git a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs
index 84b3e220ff..15ab8f244b 100644
--- a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs
+++ b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs
@@ -138,7 +138,7 @@ namespace Umbraco.Web.Strategies
try
{
// running on a background task, requires a safe database (see UsingSafeDatabase doc)
- using (ApplicationContext.Current.DatabaseContext.UsingSafeDatabase)
+ using (ApplicationContext.Current.DatabaseContext.UseSafeDatabase(true))
{
_svc.TouchServer(_serverAddress, _svc.CurrentServerIdentity, _registrar.Options.StaleServerTimeout);
}
diff --git a/src/UmbracoExamine/DataServices/UmbracoContentService.cs b/src/UmbracoExamine/DataServices/UmbracoContentService.cs
index 4d2ea5bd9a..df0562f49c 100644
--- a/src/UmbracoExamine/DataServices/UmbracoContentService.cs
+++ b/src/UmbracoExamine/DataServices/UmbracoContentService.cs
@@ -13,7 +13,6 @@ namespace UmbracoExamine.DataServices
{
public class UmbracoContentService : IContentService
{
-
private readonly ApplicationContext _applicationContext;
public UmbracoContentService()
@@ -58,7 +57,7 @@ namespace UmbracoExamine.DataServices
[Obsolete("This should no longer be used, latest content will be indexed by using the IContentService directly")]
public XDocument GetLatestContentByXPath(string xpath)
{
- using (_applicationContext.DatabaseContext.UsingSafeDatabase)
+ using (_applicationContext.DatabaseContext.UseSafeDatabase()) // reuse current db if any else use & dispose one
{
var xmlContent = XDocument.Parse("");
var rootContent = _applicationContext.Services.ContentService.GetRootContent();
@@ -80,7 +79,7 @@ namespace UmbracoExamine.DataServices
///
public bool IsProtected(int nodeId, string path)
{
- using (_applicationContext.DatabaseContext.UsingSafeDatabase)
+ using (_applicationContext.DatabaseContext.UseSafeDatabase()) // reuse current db if any else use & dispose one
{
return _applicationContext.Services.PublicAccessService.IsProtected(path.EnsureEndsWith("," + nodeId));
}
@@ -93,8 +92,8 @@ namespace UmbracoExamine.DataServices
public IEnumerable GetAllUserPropertyNames()
{
- using (_applicationContext.DatabaseContext.UsingSafeDatabase)
- {
+ using (_applicationContext.DatabaseContext.UseSafeDatabase()) // reuse current db if any else use & dispose one
+ {
try
{
var result = _applicationContext.DatabaseContext.Database.Fetch("select distinct alias from cmsPropertyType order by alias");