using System; using System.Collections.Generic; using System.Data; using System.Linq; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence { internal class LockingRepository where TRepository : IDisposable, IRepository { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly Func _repositoryFactory; private readonly int[] _readLockIds, _writeLockIds; public LockingRepository(IDatabaseUnitOfWorkProvider uowProvider, Func repositoryFactory, IEnumerable readLockIds, IEnumerable writeLockIds) { Mandate.ParameterNotNull(uowProvider, "uowProvider"); Mandate.ParameterNotNull(repositoryFactory, "repositoryFactory"); _uowProvider = uowProvider; _repositoryFactory = repositoryFactory; _readLockIds = readLockIds == null ? new int[0] : readLockIds.ToArray(); _writeLockIds = writeLockIds == null ? new int[0] : writeLockIds.ToArray(); } public void WithReadLocked(Action> action, bool autoCommit = true) { var uow = _uowProvider.GetUnitOfWork(); using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) { foreach (var lockId in _readLockIds) uow.Database.AcquireLockNodeReadLock(lockId); using (var repository = _repositoryFactory(uow)) { action(new LockedRepository(transaction, uow, repository)); if (autoCommit == false) return; uow.Commit(); transaction.Complete(); } } } public TResult WithReadLocked(Func, TResult> func, bool autoCommit = true) { var uow = _uowProvider.GetUnitOfWork(); using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) { foreach (var lockId in _readLockIds) uow.Database.AcquireLockNodeReadLock(lockId); using (var repository = _repositoryFactory(uow)) { var ret = func(new LockedRepository(transaction, uow, repository)); if (autoCommit == false) return ret; uow.Commit(); transaction.Complete(); return ret; } } } public void WithWriteLocked(Action> action, bool autoCommit = true) { var uow = _uowProvider.GetUnitOfWork(); using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) { foreach (var lockId in _writeLockIds) uow.Database.AcquireLockNodeWriteLock(lockId); using (var repository = _repositoryFactory(uow)) { action(new LockedRepository(transaction, uow, repository)); if (autoCommit == false) return; uow.Commit(); transaction.Complete(); } } } public TResult WithWriteLocked(Func, TResult> func, bool autoCommit = true) { var uow = _uowProvider.GetUnitOfWork(); using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) { foreach (var lockId in _writeLockIds) uow.Database.AcquireLockNodeReadLock(lockId); using (var repository = _repositoryFactory(uow)) { var ret = func(new LockedRepository(transaction, uow, repository)); if (autoCommit == false) return ret; uow.Commit(); transaction.Complete(); return ret; } } } } }