U4-9201 - fix it all

This commit is contained in:
Stephan
2016-11-23 15:26:41 +01:00
parent 3300dbbe5e
commit 0689bf4f31
7 changed files with 56 additions and 43 deletions

View File

@@ -94,13 +94,13 @@ namespace Umbraco.Core
public ISqlSyntaxProvider SqlSyntax { get; private set; }
/// <summary>
/// Gets the <see cref="Database"/> 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.
/// </summary>
/// <remarks>
/// <para>Should not be used for operation against standard Umbraco tables; as services should be used instead.</para>
/// <para>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.</para>
/// 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).</para>
/// </remarks>
public virtual UmbracoDatabase Database
{
@@ -108,24 +108,32 @@ namespace Umbraco.Core
}
/// <summary>
/// Replaces the "ambient" database (if any) and installs a new <see cref="Database"/> object.
/// Replaces the "ambient" database (if any) and by a new temp database.
/// </summary>
/// <remarks>
/// <para>The returned IDisposable *must* be diposed in order to properly disposed the safe database and
/// <para>The returned IDisposable *must* be diposed in order to properly dispose the temp database and
/// restore the original "ambient" database (if any).</para>
/// <para>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.</para>
/// </remarks>
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());
}
/// <summary>
@@ -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<DatabaseContext>("Detached db " + (_database == null ? "x" : _database.InstanceSid) + ".");
DefaultDatabaseFactory.AttachDatabase(database);
_logger.Debug<DatabaseContext>("Attached db " + database.InstanceSid + ".");
_orig = orig;
_temp = temp;
}
public void Dispose()
{
// restore the original DB
var db = DefaultDatabaseFactory.DetachDatabase();
_logger.Debug<DatabaseContext>("Detached db " + db.InstanceSid + ".");
db.Dispose();
DefaultDatabaseFactory.AttachDatabase(_database);
_logger.Debug<DatabaseContext>("Attached db " + (_database == null ? "x" : _database.InstanceSid) + ".");
if (_temp != null)
{
_temp.Dispose();
if (_orig != null)
DefaultDatabaseFactory.AttachAmbientDatabase(_orig);
}
GC.SuppressFinalize(this);
}
}
}

View File

@@ -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");

View File

@@ -21,7 +21,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;DEBUG_DATABASES</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>

View File

@@ -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<LogScrubber>("Log scrubbing executing", "Log scrubbing complete"))
{
Log.CleanLogs(GetLogScrubbingMaximumAge(_settings));

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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("<content></content>");
var rootContent = _applicationContext.Services.ContentService.GetRootContent();
@@ -80,7 +79,7 @@ namespace UmbracoExamine.DataServices
/// <returns></returns>
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<string> 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<string>("select distinct alias from cmsPropertyType order by alias");