diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs
index 302eb75b99..0227467cb0 100644
--- a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs
+++ b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs
@@ -86,18 +86,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
///
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("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);
}
///
@@ -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()
diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs
index 1c7d0dae43..82cdc71b4f 100644
--- a/src/Umbraco.Core/Scoping/IScope.cs
+++ b/src/Umbraco.Core/Scoping/IScope.cs
@@ -15,6 +15,11 @@ namespace Umbraco.Core.Scoping
///
IUmbracoDatabase Database { get; }
+ ///
+ /// Gets the database context.
+ ///
+ IDatabaseContext DatabaseContext { get; }
+
///
/// Gets the scope event messages.
///
@@ -41,5 +46,17 @@ namespace Umbraco.Core.Scoping
/// A value indicating whether the scope has been successfully completed.
/// Can return false if any child scope has not completed.
bool Complete();
+
+ ///
+ /// Read-locks some lock objects.
+ ///
+ /// The lock object identifiers.
+ void ReadLock(params int[] lockIds);
+
+ ///
+ /// Write-locks some lock objects.
+ ///
+ /// The lock object identifiers.
+ void WriteLock(params int[] lockIds);
}
}
diff --git a/src/Umbraco.Core/Scoping/IScopeProvider.cs b/src/Umbraco.Core/Scoping/IScopeProvider.cs
index fa8cbf72e5..cc35790588 100644
--- a/src/Umbraco.Core/Scoping/IScopeProvider.cs
+++ b/src/Umbraco.Core/Scoping/IScopeProvider.cs
@@ -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
///
IScopeContext Context { get; }
+ ///
+ /// Gets the database context.
+ ///
+ IDatabaseContext DatabaseContext { get; }
+
#if DEBUG_SCOPES
Dictionary CallContextObjects { get; }
IEnumerable ScopeInfos { get; }
diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs
index cec84f0368..ad43a44bb9 100644
--- a/src/Umbraco.Core/Scoping/Scope.cs
+++ b/src/Umbraco.Core/Scoping/Scope.cs
@@ -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;
+
+ ///
+ 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("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.");
+ }
+ }
+
+ ///
+ 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.");
+ }
+ }
}
}
diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs
index 97e5ea5cc3..6707281fb6 100644
--- a/src/Umbraco.Core/Scoping/ScopeProvider.cs
+++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs
@@ -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