Scope/UnitOfWork db context and locking
This commit is contained in:
@@ -86,18 +86,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// <inheritdoc />
|
||||
public void ReadLock(params int[] lockIds)
|
||||
{
|
||||
// soon as we get Database, a transaction is started
|
||||
|
||||
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
|
||||
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
|
||||
|
||||
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
|
||||
foreach (var lockId in lockIds)
|
||||
{
|
||||
var i = Database.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId });
|
||||
if (i == null) // ensure we are actually locking!
|
||||
throw new Exception($"LockObject with id={lockId} does not exist.");
|
||||
}
|
||||
Scope.ReadLock(lockIds);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -106,18 +95,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
if (ReadOnly)
|
||||
throw new NotSupportedException("This unit of work is read-only.");
|
||||
|
||||
// soon as we get Database, a transaction is started
|
||||
|
||||
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
|
||||
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
|
||||
|
||||
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
|
||||
foreach (var lockId in lockIds)
|
||||
{
|
||||
var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId });
|
||||
if (i == 0) // ensure we are actually locking!
|
||||
throw new Exception($"LockObject with id={lockId} does not exist.");
|
||||
}
|
||||
Scope.WriteLock(lockIds);
|
||||
}
|
||||
|
||||
public override void Complete()
|
||||
|
||||
@@ -15,6 +15,11 @@ namespace Umbraco.Core.Scoping
|
||||
/// </summary>
|
||||
IUmbracoDatabase Database { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context.
|
||||
/// </summary>
|
||||
IDatabaseContext DatabaseContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scope event messages.
|
||||
/// </summary>
|
||||
@@ -41,5 +46,17 @@ namespace Umbraco.Core.Scoping
|
||||
/// <returns>A value indicating whether the scope has been successfully completed.</returns>
|
||||
/// <remarks>Can return false if any child scope has not completed.</remarks>
|
||||
bool Complete();
|
||||
|
||||
/// <summary>
|
||||
/// Read-locks some lock objects.
|
||||
/// </summary>
|
||||
/// <param name="lockIds">The lock object identifiers.</param>
|
||||
void ReadLock(params int[] lockIds);
|
||||
|
||||
/// <summary>
|
||||
/// Write-locks some lock objects.
|
||||
/// </summary>
|
||||
/// <param name="lockIds">The lock object identifiers.</param>
|
||||
void WriteLock(params int[] lockIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
using System.Collections.Generic;
|
||||
#endif
|
||||
@@ -76,6 +78,11 @@ namespace Umbraco.Core.Scoping
|
||||
/// </summary>
|
||||
IScopeContext Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context.
|
||||
/// </summary>
|
||||
IDatabaseContext DatabaseContext { get; }
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
Dictionary<Guid, object> CallContextObjects { get; }
|
||||
IEnumerable<ScopeInfo> ScopeInfos { get; }
|
||||
|
||||
@@ -136,6 +136,8 @@ namespace Umbraco.Core.Scoping
|
||||
|
||||
public Guid InstanceId { get; } = Guid.NewGuid();
|
||||
|
||||
public IDatabaseContext DatabaseContext => _scopeProvider.DatabaseContext;
|
||||
|
||||
// a value indicating whether to force call-context
|
||||
public bool CallContext
|
||||
{
|
||||
@@ -481,5 +483,39 @@ namespace Umbraco.Core.Scoping
|
||||
// true if Umbraco.CoreDebug.LogUncompletedScope appSetting is set to "true"
|
||||
private static bool LogUncompletedScopes => (_logUncompletedScopes
|
||||
?? (_logUncompletedScopes = UmbracoConfig.For.CoreDebug().LogUncompletedScopes)).Value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ReadLock(params int[] lockIds)
|
||||
{
|
||||
// soon as we get Database, a transaction is started
|
||||
|
||||
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
|
||||
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
|
||||
|
||||
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
|
||||
foreach (var lockId in lockIds)
|
||||
{
|
||||
var i = Database.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId });
|
||||
if (i == null) // ensure we are actually locking!
|
||||
throw new Exception($"LockObject with id={lockId} does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void WriteLock(params int[] lockIds)
|
||||
{
|
||||
// soon as we get Database, a transaction is started
|
||||
|
||||
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
|
||||
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
|
||||
|
||||
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
|
||||
foreach (var lockId in lockIds)
|
||||
{
|
||||
var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId });
|
||||
if (i == 0) // ensure we are actually locking!
|
||||
throw new Exception($"LockObject with id={lockId} does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace Umbraco.Core.Scoping
|
||||
|
||||
public IUmbracoDatabaseFactory DatabaseFactory { get; }
|
||||
|
||||
public IDatabaseContext DatabaseContext => DatabaseFactory;
|
||||
|
||||
#region Context
|
||||
|
||||
// objects that go into the logical call context better be serializable else they'll eventually
|
||||
|
||||
Reference in New Issue
Block a user