From be32448426f78f9c7ae438934206fe7dfd3df9fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 2 May 2016 12:12:21 +0200 Subject: [PATCH] U4-6147 - cleanup persistence units of work (in progress) --- .../Persistence/LockedRepository.cs | 28 --- .../Persistence/LockingRepository.cs | 103 ---------- .../Repositories/FileRepository.cs | 10 +- .../Repositories/RepositoryBase.cs | 6 +- .../Persistence/TransactionType.cs | 12 -- .../Persistence/UnitOfWork/FileUnitOfWork.cs | 125 +----------- .../UnitOfWork/IDatabaseUnitOfWork.cs | 6 +- .../Persistence/UnitOfWork/IUnitOfWork.cs | 66 ++++++- .../Persistence/UnitOfWork/NPocoUnitOfWork.cs | 186 ++++-------------- .../Persistence/UnitOfWork/UnitOfWorkBase.cs | 137 +++++++++++++ src/Umbraco.Core/Services/AuditService.cs | 2 +- src/Umbraco.Core/Services/ContentService.cs | 111 +++++------ .../Services/ContentTypeService.cs | 36 ++-- src/Umbraco.Core/Services/DataTypeService.cs | 60 +++--- src/Umbraco.Core/Services/DomainService.cs | 12 +- .../Services/ExternalLoginService.cs | 6 +- src/Umbraco.Core/Services/FileService.cs | 149 +++++++------- .../Services/LocalizationService.cs | 40 ++-- src/Umbraco.Core/Services/MacroService.cs | 16 +- src/Umbraco.Core/Services/MediaService.cs | 35 ++-- .../Services/MemberGroupService.cs | 4 +- src/Umbraco.Core/Services/MemberService.cs | 10 +- .../Services/MemberTypeService.cs | 12 +- .../Services/MigrationEntryService.cs | 2 +- .../Services/PublicAccessService.cs | 34 ++-- src/Umbraco.Core/Services/RelationService.cs | 17 +- .../Services/ServerRegistrationService.cs | 6 +- src/Umbraco.Core/Services/TaskService.cs | 8 +- src/Umbraco.Core/Services/UserService.cs | 26 +-- src/Umbraco.Core/Umbraco.Core.csproj | 4 +- .../Repositories/AuditRepositoryTest.cs | 2 +- .../Repositories/ContentRepositoryTest.cs | 55 +++--- .../Repositories/ContentTypeRepositoryTest.cs | 78 ++++---- .../DataTypeDefinitionRepositoryTest.cs | 46 ++--- .../Repositories/DictionaryRepositoryTest.cs | 16 +- .../Repositories/DomainRepositoryTest.cs | 38 ++-- .../Repositories/LanguageRepositoryTest.cs | 10 +- .../Repositories/MacroRepositoryTest.cs | 30 +-- .../Repositories/MediaRepositoryTest.cs | 18 +- .../Repositories/MediaTypeRepositoryTest.cs | 42 ++-- .../Repositories/MemberRepositoryTest.cs | 26 +-- .../Repositories/MemberTypeRepositoryTest.cs | 32 +-- .../PartialViewRepositoryTests.cs | 6 +- .../PublicAccessRepositoryTest.cs | 22 +-- .../Repositories/RelationRepositoryTest.cs | 10 +- .../RelationTypeRepositoryTest.cs | 8 +- .../Repositories/ScriptRepositoryTest.cs | 22 +-- .../ServerRegistrationRepositoryTest.cs | 12 +- .../Repositories/StylesheetRepositoryTest.cs | 26 +-- .../Repositories/TagRepositoryTest.cs | 104 +++++----- .../Repositories/TaskRepositoryTest.cs | 14 +- .../Repositories/TaskTypeRepositoryTest.cs | 4 +- .../Repositories/TemplateRepositoryTest.cs | 62 +++--- .../Repositories/UserRepositoryTest.cs | 32 +-- .../Repositories/UserTypeRepositoryTest.cs | 20 +- .../Services/ContentServiceTests.cs | 2 +- .../Services/MacroServiceTests.cs | 2 +- 57 files changed, 895 insertions(+), 1113 deletions(-) delete mode 100644 src/Umbraco.Core/Persistence/LockedRepository.cs delete mode 100644 src/Umbraco.Core/Persistence/LockingRepository.cs delete mode 100644 src/Umbraco.Core/Persistence/TransactionType.cs create mode 100644 src/Umbraco.Core/Persistence/UnitOfWork/UnitOfWorkBase.cs diff --git a/src/Umbraco.Core/Persistence/LockedRepository.cs b/src/Umbraco.Core/Persistence/LockedRepository.cs deleted file mode 100644 index c067e84288..0000000000 --- a/src/Umbraco.Core/Persistence/LockedRepository.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using NPoco; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.UnitOfWork; - -namespace Umbraco.Core.Persistence -{ - internal class LockedRepository - where TRepository : IDisposable, IRepository - { - public LockedRepository(ITransaction transaction, IDatabaseUnitOfWork unitOfWork, TRepository repository) - { - Transaction = transaction; - UnitOfWork = unitOfWork; - Repository = repository; - } - - public ITransaction Transaction { get; private set; } - public IDatabaseUnitOfWork UnitOfWork { get; private set; } - public TRepository Repository { get; private set; } - - public void Commit() - { - UnitOfWork.Commit(); - Transaction.Complete(); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/LockingRepository.cs b/src/Umbraco.Core/Persistence/LockingRepository.cs deleted file mode 100644 index f513073e71..0000000000 --- a/src/Umbraco.Core/Persistence/LockingRepository.cs +++ /dev/null @@ -1,103 +0,0 @@ -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; - } - } - } - } -} diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs index e1e4c3105a..32af5a55ad 100644 --- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs @@ -36,12 +36,12 @@ namespace Umbraco.Core.Persistence.Repositories public virtual void AddFolder(string folderPath) { - _work.RegisterAdded(new Folder(folderPath), this); + _work.RegisterCreated(new Folder(folderPath), this); } public virtual void DeleteFolder(string folderPath) { - _work.RegisterRemoved(new Folder(folderPath), this); + _work.RegisterDeleted(new Folder(folderPath), this); } #region Implementation of IRepository @@ -50,11 +50,11 @@ namespace Umbraco.Core.Persistence.Repositories { if (FileSystem.FileExists(entity.OriginalPath) == false) { - _work.RegisterAdded(entity, this); + _work.RegisterCreated(entity, this); } else { - _work.RegisterChanged(entity, this); + _work.RegisterUpdated(entity, this); } } @@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories { if (_work != null) { - _work.RegisterRemoved(entity, this); + _work.RegisterDeleted(entity, this); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 660cf07b74..732d77d2c4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -112,11 +112,11 @@ namespace Umbraco.Core.Persistence.Repositories { if (entity.HasIdentity == false) { - UnitOfWork.RegisterAdded(entity, this); + UnitOfWork.RegisterCreated(entity, this); } else { - UnitOfWork.RegisterChanged(entity, this); + UnitOfWork.RegisterUpdated(entity, this); } } @@ -126,7 +126,7 @@ namespace Umbraco.Core.Persistence.Repositories /// public virtual void Delete(TEntity entity) { - UnitOfWork?.RegisterRemoved(entity, this); + UnitOfWork?.RegisterDeleted(entity, this); } protected abstract TEntity PerformGet(TId id); diff --git a/src/Umbraco.Core/Persistence/TransactionType.cs b/src/Umbraco.Core/Persistence/TransactionType.cs deleted file mode 100644 index 3f2fa9b87a..0000000000 --- a/src/Umbraco.Core/Persistence/TransactionType.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Umbraco.Core.Persistence -{ - /// - /// Enum for the 3 types of transactions - /// - internal enum TransactionType - { - Insert, - Update, - Delete - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs index 82000248d1..d7562e716f 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs @@ -1,15 +1,13 @@ -using System.Collections.Generic; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Repositories; - -namespace Umbraco.Core.Persistence.UnitOfWork +namespace Umbraco.Core.Persistence.UnitOfWork { /// - /// Represents the Unit of Work implementation for working with files + /// Represents a persistence unit of work for working with files. /// - internal class FileUnitOfWork : DisposableObject, IUnitOfWork + /// The FileUnitOfWork does *not* implement transactions, so although it needs to be flushed or + /// completed for operations to be executed, they are executed immediately without any commit or roll back + /// mechanism. + internal class FileUnitOfWork : UnitOfWorkBase { - private readonly Queue _operations = new Queue(); private readonly RepositoryFactory _factory; /// @@ -28,118 +26,9 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// The type of the repository. /// The optional name of the repository. /// The created repository for the unit of work. - public TRepository CreateRepository(string name = null) - where TRepository : IRepository + public override TRepository CreateRepository(string name = null) { return _factory.CreateRepository(this, name); } - - /// - /// Registers an instance to be added through this . - /// - /// The entity. - /// The repository participating in the transaction. - public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue(new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Insert - }); - } - - /// - /// Registers an instance to be changed through this . - /// - /// The entity. - /// The repository participating in the transaction. - public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue(new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Update - }); - } - - /// - /// Registers an instance to be removed through this . - /// - /// The entity. - /// The repository participating in the transaction. - public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue(new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Delete - }); - } - - public void Commit() - { - //NOTE: I'm leaving this in here for reference, but this is useless, transaction scope + Files doesn't do anything, - // the closest you can get is transactional NTFS, but that requires distributed transaction coordinator and some other libs/wrappers, - // plus MS has not deprecated it anyways. To do transactional IO we'd have to write this ourselves using temporary files and then - // on committing move them to their correct place. - //using(var scope = new TransactionScope()) - //{ - // // Commit the transaction - // scope.Complete(); - //} - - while (_operations.Count > 0) - { - var operation = _operations.Dequeue(); - switch (operation.Type) - { - case TransactionType.Insert: - operation.Repository.PersistNewItem(operation.Entity); - break; - case TransactionType.Delete: - operation.Repository.PersistDeletedItem(operation.Entity); - break; - case TransactionType.Update: - operation.Repository.PersistUpdatedItem(operation.Entity); - break; - } - } - - // clear everything - // fixme - why? everything should have been dequeued? - _operations.Clear(); - } - - protected override void DisposeResources() - { - _operations.Clear(); - } - - /// - /// Provides a snapshot of an entity and the repository reference it belongs to. - /// - private sealed class Operation - { - /// - /// Gets or sets the entity. - /// - /// The entity. - public IEntity Entity { get; set; } - - /// - /// Gets or sets the repository. - /// - /// The repository. - public IUnitOfWorkRepository Repository { get; set; } - - /// - /// Gets or sets the type of operation. - /// - /// The type of operation. - public TransactionType Type { get; set; } - } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs index 025f1bfc2b..9bced69f52 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs @@ -1,11 +1,9 @@ -using System; - namespace Umbraco.Core.Persistence.UnitOfWork { /// - /// Defines a unit of work when working with a database object + /// Represents a persistence unit of work for working with a database. /// - public interface IDatabaseUnitOfWork : IUnitOfWork, IDisposable + public interface IDatabaseUnitOfWork : IUnitOfWork { UmbracoDatabase Database { get; } diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWork.cs index b8242795a4..a3ec517546 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWork.cs @@ -5,14 +5,70 @@ using Umbraco.Core.Persistence.Repositories; namespace Umbraco.Core.Persistence.UnitOfWork { /// - /// Defines a Unit Of Work + /// Represents a persistence unit of work. /// public interface IUnitOfWork : IDisposable { - void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository); - void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository); - void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository); - void Commit(); + /// + /// Registers an entity to be created as part of this unit of work. + /// + /// The entity. + /// The repository in charge of the entity. + void RegisterCreated(IEntity entity, IUnitOfWorkRepository repository); + + /// + /// Registers an entity to be updated as part of this unit of work. + /// + /// The entity. + /// The repository in charge of the entity. + void RegisterUpdated(IEntity entity, IUnitOfWorkRepository repository); + + /// + /// Registers an entity to be deleted as part of this unit of work. + /// + /// The entity. + /// The repository in charge of the entity. + void RegisterDeleted(IEntity entity, IUnitOfWorkRepository repository); + + /// + /// Begins the unit of work. + /// + /// When a unit of work begins, a local transaction scope is created at database level. + /// This is useful eg when reading entities before creating, updating or deleting, and the read + /// needs to be part of the transaction. Flushing or completing the unit of work automatically + /// begins the transaction (so no need to call Begin if not necessary). + void Begin(); + + /// + /// Flushes the unit of work. + /// + /// + /// When a unit of work is flushed, all queued operations are executed. This is useful eg + /// when a row needs to be created in the database, so that its auto-generated ID can be retrieved. + /// Note that it does *not* complete the unit of work, however. Completing the unit of work + /// automatically flushes the queue (so no need to call Flush if not necessary). + /// + void Flush(); + + /// + /// Completes the unit of work. + /// + /// When a unit of work is completed, a local transaction scope is created at database level, + /// all queued operations are executed, and the scope is commited. If the unit of work is not completed + /// before it is disposed, all queued operations are cleared and the scope is rolled back (and also + /// higher level transactions if any). + /// Whether this actually commits or rolls back the transaction depends on whether the transaction scope + /// is part of a higher level transactions. The database transaction is committed or rolled back only + /// when the upper level scope is disposed. + /// + void Complete(); + + /// + /// Creates a repository. + /// + /// The type of the repository. + /// The optional name of the repository. + /// The created repository for the unit of work. TRepository CreateRepository(string name = null) where TRepository : IRepository; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWork.cs index 0634affdb1..8e68f9ade3 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWork.cs @@ -1,19 +1,14 @@ using System; -using System.Collections.Generic; using System.Data; -using LightInject; using NPoco; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Repositories; namespace Umbraco.Core.Persistence.UnitOfWork { - /// - /// Represents the Unit of Work implementation for NPoco. - /// - internal class NPocoUnitOfWork : DisposableObject, IDatabaseUnitOfWork - { - private readonly Queue _operations = new Queue(); + /// + /// Implements IDatabaseUnitOfWork for NPoco. + /// + internal class NPocoUnitOfWork : UnitOfWorkBase, IDatabaseUnitOfWork + { private readonly RepositoryFactory _factory; private ITransaction _transaction; @@ -24,10 +19,10 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// A repository factory. /// This should be used by the NPocoUnitOfWorkProvider exclusively. internal NPocoUnitOfWork(UmbracoDatabase database, RepositoryFactory factory) - { - Database = database; + { + Database = database; _factory = factory; - } + } /// /// Gets the unit of work underlying database. @@ -40,175 +35,64 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// The type of the repository. /// The optional name of the repository. /// The created repository for the unit of work. - public TRepository CreateRepository(string name = null) - where TRepository : IRepository + public override TRepository CreateRepository(string name = null) { return _factory.CreateRepository(this, name); } /// - /// Registers an instance to be added through this . - /// - /// The entity. - /// The repository participating in the transaction. - public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue(new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Insert - }); - } - - /// - /// Registers an instance to be changed through this . - /// - /// The entity. - /// The repository participating in the transaction. - public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue(new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Update - }); - } - - /// - /// Registers an instance to be removed through this . - /// - /// The entity. - /// The repository participating in the transaction. - public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue(new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Delete - }); - } - - /// - /// Ensures that we have a transaction. + /// Ensures that we have a transaction. /// /// Isolation level is determined by the database, see UmbracoDatabase.DefaultIsolationLevel. Should be /// at least IsolationLevel.RepeatablRead else the node locks will not work correctly. - public void EnsureTransaction() - { - if (_transaction == null) - _transaction = Database.GetTransaction(); - } - - /// - /// Commits all batched changes. - /// - /// - /// Unlike a typical unit of work, this UOW will let you commit more than once since a new transaction is creaed per - /// Commit() call instead of having one transaction per UOW. - /// - public void Commit() - { - Commit(null); - } - - /// - /// Commits all batched changes. - /// - /// A callback which is executed after the operations have been processed and - /// before the transaction is completed. - internal void Commit(Action completing) + public override void Begin() { - EnsureTransaction(); + base.Begin(); - try - { - while (_operations.Count > 0) - { - var operation = _operations.Dequeue(); - switch (operation.Type) - { - case TransactionType.Insert: - operation.Repository.PersistNewItem(operation.Entity); - break; - case TransactionType.Delete: - operation.Repository.PersistDeletedItem(operation.Entity); - break; - case TransactionType.Update: - operation.Repository.PersistUpdatedItem(operation.Entity); - break; - } - } - - // execute the callback if there is one - completing?.Invoke(Database); - _transaction.Complete(); - } - finally - { - _transaction.Dispose(); // will rollback if not completed - _transaction = null; - } - - // clear everything - // in case the Dequeue loop was aborted - // fixme - but the, exception and this is never reached? - _operations.Clear(); + if (_transaction == null) + _transaction = Database.GetTransaction(); } protected override void DisposeResources() { - _operations.Clear(); + base.DisposeResources(); + // no transaction, nothing to do if (_transaction == null) return; - _transaction.Dispose(); + + // will either complete or abort NPoco transaction + // which means going one level up in the transaction stack + // and commit or rollback only if at top of stack + if (Completed) + _transaction.Complete(); // complete the transaction + else + _transaction.Dispose(); // abort the transaction + _transaction = null; } - public void ReadLockNodes(params int[] lockIds) - { - EnsureTransaction(); + public void ReadLockNodes(params int[] lockIds) + { + Begin(); // we need a transaction + 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) Database.ExecuteScalar("SELECT sortOrder FROM umbracoNode WHERE id=@id", new { @id = lockId }); } public void WriteLockNodes(params int[] lockIds) - { - EnsureTransaction(); + { + Begin(); // we need a transaction + 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) Database.Execute("UPDATE umbracoNode SET sortOrder = (CASE WHEN (sortOrder=1) THEN -1 ELSE 1 END) WHERE id=@id", new { @id = lockId }); } - - /// - /// Provides a snapshot of an entity and the repository reference it belongs to. - /// - private sealed class Operation - { - /// - /// Gets or sets the entity. - /// - /// The entity. - public IEntity Entity { get; set; } - - /// - /// Gets or sets the repository. - /// - /// The repository. - public IUnitOfWorkRepository Repository { get; set; } - - /// - /// Gets or sets the type of operation. - /// - /// The type of operation. - public TransactionType Type { get; set; } - } - } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/UnitOfWorkBase.cs b/src/Umbraco.Core/Persistence/UnitOfWork/UnitOfWorkBase.cs new file mode 100644 index 0000000000..16fd84aa73 --- /dev/null +++ b/src/Umbraco.Core/Persistence/UnitOfWork/UnitOfWorkBase.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Persistence.Repositories; + +namespace Umbraco.Core.Persistence.UnitOfWork +{ + public abstract class UnitOfWorkBase : DisposableObject, IUnitOfWork + { + private readonly Queue _operations = new Queue(); + + // fixme eventually kill this + public virtual T CreateRepository(string name = null) where T:IRepository { throw new NotImplementedException(); } + + /// + /// Registers an entity to be added as part of this unit of work. + /// + /// The entity. + /// The repository in charge of the entity. + public void RegisterCreated(IEntity entity, IUnitOfWorkRepository repository) + { + Completed = false; + _operations.Enqueue(new Operation + { + Entity = entity, + Repository = repository, + Type = OperationType.Insert + }); + } + + /// + /// Registers an entity to be updated as part of this unit of work. + /// + /// The entity. + /// The repository in charge of the entity. + public void RegisterUpdated(IEntity entity, IUnitOfWorkRepository repository) + { + Completed = false; + _operations.Enqueue(new Operation + { + Entity = entity, + Repository = repository, + Type = OperationType.Update + }); + } + + /// + /// Registers an entity to be deleted as part of this unit of work. + /// + /// The entity. + /// The repository in charge of the entity. + public void RegisterDeleted(IEntity entity, IUnitOfWorkRepository repository) + { + Completed = false; + _operations.Enqueue(new Operation + { + Entity = entity, + Repository = repository, + Type = OperationType.Delete + }); + } + + public virtual void Begin() + { } + + public virtual void Flush() + { + Begin(); + + while (_operations.Count > 0) + { + var operation = _operations.Dequeue(); + switch (operation.Type) + { + case OperationType.Insert: + operation.Repository.PersistNewItem(operation.Entity); + break; + case OperationType.Delete: + operation.Repository.PersistDeletedItem(operation.Entity); + break; + case OperationType.Update: + operation.Repository.PersistUpdatedItem(operation.Entity); + break; + } + } + } + + public virtual void Complete() + { + Flush(); + Completed = true; + } + + protected bool Completed { get; private set; } + + protected override void DisposeResources() + { + // whatever hasn't been commited is lost + // not sure we need this as we are being disposed... + _operations.Clear(); + } + + /// + /// Provides a snapshot of an entity and the repository reference it belongs to. + /// + private sealed class Operation + { + /// + /// Gets or sets the entity. + /// + /// The entity. + public IEntity Entity { get; set; } + + /// + /// Gets or sets the repository. + /// + /// The repository. + public IUnitOfWorkRepository Repository { get; set; } + + /// + /// Gets or sets the type of operation. + /// + /// The type of operation. + public OperationType Type { get; set; } + } + + /// + /// The types of unit of work operation. + /// + private enum OperationType + { + Insert, + Update, + Delete + } + } +} diff --git a/src/Umbraco.Core/Services/AuditService.cs b/src/Umbraco.Core/Services/AuditService.cs index 82b5222c34..cde9e03a29 100644 --- a/src/Umbraco.Core/Services/AuditService.cs +++ b/src/Umbraco.Core/Services/AuditService.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); repo.AddOrUpdate(new AuditItem(objectId, comment, type, userId)); - uow.Commit(); + uow.Complete(); } } diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index a1658fed06..b8a87fe313 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -35,7 +35,7 @@ namespace Umbraco.Core.Services private readonly IUserService _userService; private readonly IEnumerable _urlSegmentProviders; - //Support recursive locks because some of the methods that require locking call other methods that require locking. + //Support recursive locks because some of the methods that require locking call other methods that require locking. //for example, the Move method needs to be locked but this calls the Save method which also needs to be locked. private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); @@ -175,8 +175,8 @@ namespace Umbraco.Core.Services using (var uow = UowProvider.GetUnitOfWork()) { var repo = uow.CreateRepository(); - repo.AddOrUpdate(new AuditItem(content.Id, string.Format("Content '{0}' was created", name), AuditType.New, content.CreatorId)); - uow.Commit(); + repo.AddOrUpdate(new AuditItem(content.Id, $"Content '{name}' was created", AuditType.New, content.CreatorId)); + uow.Complete(); } return content; @@ -252,22 +252,20 @@ namespace Umbraco.Core.Services return content; } + content.CreatorId = userId; + content.WriterId = userId; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - content.CreatorId = userId; - content.WriterId = userId; repository.AddOrUpdate(content); - //Generate a new preview repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, c)); - uow.Commit(); + uow.Complete(); } Saved.RaiseEvent(new SaveEventArgs(content, false), this); - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); - - Audit(AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); + Audit(AuditType.New, $"Content '{name}' was created with Id {content.Id}", content.CreatorId, content.Id); return content; } @@ -306,22 +304,20 @@ namespace Umbraco.Core.Services return content; } + content.CreatorId = userId; + content.WriterId = userId; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - content.CreatorId = userId; - content.WriterId = userId; repository.AddOrUpdate(content); - //Generate a new preview repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, c)); - uow.Commit(); + uow.Complete(); } Saved.RaiseEvent(new SaveEventArgs(content, false), this); - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this); - - Audit(AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); + Audit(AuditType.New, $"Content '{name}' was created with Id {content.Id}", content.CreatorId, content.Id); return content; } @@ -557,7 +553,7 @@ namespace Umbraco.Core.Services /// Field to order by /// Direction to order by /// Search text filter - /// An Enumerable list of objects + /// An Enumerable list of objects public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") { return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter); @@ -574,7 +570,7 @@ namespace Umbraco.Core.Services /// Direction to order by /// Flag to indicate when ordering by system field /// Search text filter - /// An Enumerable list of objects + /// An Enumerable list of objects public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, string filter) { Mandate.ParameterCondition(pageIndex >= 0, "pageIndex"); @@ -830,12 +826,12 @@ namespace Umbraco.Core.Services } /// - /// This will rebuild the xml structures for content in the database. + /// This will rebuild the xml structures for content in the database. /// /// This is not used for anything /// True if publishing succeeded, otherwise False /// - /// This is used for when a document type alias or a document type property is changed, the xml will need to + /// This is used for when a document type alias or a document type property is changed, the xml will need to /// be regenerated. /// public bool RePublishAll(int userId = 0) @@ -853,7 +849,7 @@ namespace Umbraco.Core.Services } /// - /// This will rebuild the xml structures for content in the database. + /// This will rebuild the xml structures for content in the database. /// /// /// If specified will only rebuild the xml for the content type's specified, otherwise will update the structure @@ -949,28 +945,27 @@ namespace Umbraco.Core.Services UnPublish(descendant, userId); } + content.WriterId = userId; + content.ChangeTrashedState(true); + using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - content.WriterId = userId; - content.ChangeTrashedState(true); repository.AddOrUpdate(content); //Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId foreach (var descendant in descendants) { moveInfo.Add(new MoveEventInfo(descendant, descendant.Path, descendant.ParentId)); - descendant.WriterId = userId; descendant.ChangeTrashedState(true, descendant.ParentId); repository.AddOrUpdate(descendant); } - uow.Commit(); + uow.Complete(); } Trashed.RaiseEvent(new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this); - Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id); return OperationStatus.Success(evtMsgs); @@ -1080,10 +1075,10 @@ namespace Umbraco.Core.Services /// /// Saves a collection of objects. - /// + /// /// Collection of to save /// Optional Id of the User saving the Content - /// Optional boolean indicating whether or not to raise events. + /// Optional boolean indicating whether or not to raise events. Attempt IContentServiceOperations.Save(IEnumerable contents, int userId, bool raiseEvents) { var asArray = contents.ToArray(); @@ -1132,12 +1127,11 @@ namespace Umbraco.Core.Services } } - uow.Commit(); + uow.Complete(); } if (raiseEvents) Saved.RaiseEvent(new SaveEventArgs(asArray, false, evtMsgs), this); - Audit(AuditType.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, Constants.System.Root); return OperationStatus.Success(evtMsgs); @@ -1183,7 +1177,7 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.Delete(content); - uow.Commit(); + uow.Complete(); var args = new DeleteEventArgs(content, false, evtMsgs); Deleted.RaiseEvent(args, this); @@ -1243,10 +1237,10 @@ namespace Umbraco.Core.Services /// Optional Id of the user issueing the delete operation public void DeleteContentOfType(int contentTypeId, int userId = 0) { - //TODO: This currently this is called from the ContentTypeService but that needs to change, + //TODO: This currently this is called from the ContentTypeService but that needs to change, // if we are deleting a content type, we should just delete the data and do this operation slightly differently. // This method will recursively go lookup every content item, check if any of it's descendants are - // of a different type, move them to the recycle bin, then permanently delete the content items. + // of a different type, move them to the recycle bin, then permanently delete the content items. // The main problem with this is that for every content item being deleted, events are raised... // which we need for many things like keeping caches in sync, but we can surely do this MUCH better. @@ -1316,7 +1310,7 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.DeleteVersions(id, versionDate); - uow.Commit(); + uow.Complete(); } DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate), this); @@ -1349,7 +1343,7 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.DeleteVersion(versionId); - uow.Commit(); + uow.Complete(); } DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, specificVersion: versionId), this); @@ -1448,7 +1442,7 @@ namespace Umbraco.Core.Services } /// - /// Copies an object by creating a new Content object of the same type and copies all data from the current + /// Copies an object by creating a new Content object of the same type and copies all data from the current /// to the new copy which is returned. Recursively copies all children. /// /// The to copy @@ -1462,7 +1456,7 @@ namespace Umbraco.Core.Services } /// - /// Copies an object by creating a new Content object of the same type and copies all data from the current + /// Copies an object by creating a new Content object of the same type and copies all data from the current /// to the new copy which is returned. /// /// The to copy @@ -1497,7 +1491,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdate(copy); //add or update a preview repository.AddOrUpdatePreviewXml(copy, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, c)); - uow.Commit(); + uow.Flush(); // ensure copy has an ID //Special case for the associated tags @@ -1512,6 +1506,8 @@ namespace Umbraco.Core.Services uow.Database.Insert(new TagRelationshipDto { NodeId = copy.Id, TagId = tag.TagId, PropertyTypeId = tag.PropertyTypeId }); } } + + uow.Complete(); } if (recursive) @@ -1574,21 +1570,19 @@ namespace Umbraco.Core.Services if (RollingBack.IsRaisedEventCancelled(new RollbackEventArgs(content), this)) return content; + content.WriterId = userId; + content.CreatorId = userId; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - content.WriterId = userId; - content.CreatorId = userId; content.ChangePublishedState(PublishedState.Unpublished); - repository.AddOrUpdate(content); - //add or update a preview repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, c)); - uow.Commit(); + uow.Complete(); } RolledBack.RaiseEvent(new RollbackEventArgs(content, false), this); - Audit(AuditType.RollBack, "Content rollback performed by user", content.WriterId, content.Id); return content; @@ -1659,7 +1653,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdateContentXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, c)); } - uow.Commit(); + uow.Complete(); } } @@ -1722,8 +1716,7 @@ namespace Umbraco.Core.Services repository.RebuildXmlStructures( content => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, content), contentTypeIds: contentTypeIds.Length == 0 ? null : contentTypeIds); - - uow.Commit(); + uow.Complete(); } Audit(AuditType.Publish, "ContentService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, Constants.System.Root); @@ -1759,7 +1752,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); repo.AddOrUpdate(new AuditItem(objectId, message, type, userId)); - uow.Commit(); + uow.Complete(); } } @@ -1807,7 +1800,7 @@ namespace Umbraco.Core.Services //TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine Save(content, false, userId); - //TODO: This shouldn't be here! This needs to be part of the repository logic but in order to fix this we need to + //TODO: This shouldn't be here! This needs to be part of the repository logic but in order to fix this we need to // change how this method calls "Save" as it needs to save using an internal method using (var uow = UowProvider.GetUnitOfWork()) { @@ -1845,7 +1838,7 @@ namespace Umbraco.Core.Services /// /// The to publish along with its children /// Optional Id of the User issueing the publishing - /// If set to true, this will also publish descendants that are completely unpublished, normally this will only publish children that have previously been published + /// If set to true, this will also publish descendants that are completely unpublished, normally this will only publish children that have previously been published /// /// A list of publish statues. If the parent document is not valid or cannot be published because it's parent(s) is not published /// then the list will only contain one status item, otherwise it will contain status items for it and all of it's descendants that @@ -1922,7 +1915,7 @@ namespace Umbraco.Core.Services updated.Add(item.Result.ContentItem); } - uow.Commit(); + uow.Complete(); } //Save xml to db and call following method to fire event: @@ -1959,17 +1952,17 @@ namespace Umbraco.Core.Services var unpublished = _publishingStrategy.UnPublish(content, userId); if (unpublished == false) return Attempt.Fail(new UnPublishStatus(content, UnPublishedStatusType.FailedCancelledByEvent, evtMsgs)); + content.WriterId = userId; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - content.WriterId = userId; repository.AddOrUpdate(content); // is published is not newest, reset the published flag on published version if (published.Version != content.Version) repository.ClearPublished(published); repository.DeleteContentXml(content); - - uow.Commit(); + uow.Complete(); } //Delete xml from db? and call following method to fire event through PublishingStrategy to update cache if (omitCacheRefresh == false) @@ -2054,7 +2047,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdateContentXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, c)); } - uow.Commit(); + uow.Complete(); } if (raiseEvents) @@ -2121,7 +2114,7 @@ namespace Umbraco.Core.Services //Generate a new preview repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, c)); - uow.Commit(); + uow.Complete(); } if (raiseEvents) @@ -2282,7 +2275,7 @@ namespace Umbraco.Core.Services #region Event Handlers /// /// Occurs before Delete - /// + /// public static event TypedEventHandler> Deleting; /// @@ -2292,7 +2285,7 @@ namespace Umbraco.Core.Services /// /// Occurs before Delete Versions - /// + /// public static event TypedEventHandler DeletingVersions; /// diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index e81cd75819..172f51bcc1 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -67,7 +67,7 @@ namespace Umbraco.Core.Services } repo.AddOrUpdate(container); - uow.Commit(); + uow.Complete(); SavedContentTypeContainer.RaiseEvent(new SaveEventArgs(container, evtMsgs), this); //TODO: Audit trail ? @@ -104,7 +104,7 @@ namespace Umbraco.Core.Services } repo.AddOrUpdate(container); - uow.Commit(); + uow.Complete(); SavedMediaTypeContainer.RaiseEvent(new SaveEventArgs(container, evtMsgs), this); //TODO: Audit trail ? @@ -164,7 +164,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateContainerRepository(containerObjectType); repo.AddOrUpdate(container); - uow.Commit(); + uow.Complete(); } savedEvent.RaiseEvent(new SaveEventArgs(container, evtMsgs), this); @@ -297,7 +297,7 @@ namespace Umbraco.Core.Services } repo.Delete(container); - uow.Commit(); + uow.Complete(); DeletedContentTypeContainer.RaiseEvent(new DeleteEventArgs(container, evtMsgs), this); @@ -323,7 +323,7 @@ namespace Umbraco.Core.Services } repo.Delete(container); - uow.Commit(); + uow.Complete(); DeletedMediaTypeContainer.RaiseEvent(new DeleteEventArgs(container, evtMsgs), this); @@ -744,7 +744,7 @@ namespace Umbraco.Core.Services contentType.CreatorId = userId; repository.AddOrUpdate(contentType); - uow.Commit(); + uow.Complete(); } UpdateContentXmlStructure(contentType); @@ -782,7 +782,7 @@ namespace Umbraco.Core.Services } //save it all in one go - uow.Commit(); + uow.Complete(); } UpdateContentXmlStructure(asArray.Cast().ToArray()); @@ -817,7 +817,7 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.Delete(contentType); - uow.Commit(); + uow.Complete(); DeletedContentType.RaiseEvent(new DeleteEventArgs(contentType, false), this); } @@ -856,7 +856,7 @@ namespace Umbraco.Core.Services repository.Delete(contentType); } - uow.Commit(); + uow.Complete(); DeletedContentType.RaiseEvent(new DeleteEventArgs(asArray, false), this); } @@ -1038,7 +1038,7 @@ namespace Umbraco.Core.Services return Attempt.Fail( new OperationStatus(ex.Operation, evtMsgs)); } - uow.Commit(); + uow.Complete(); } MovedMediaType.RaiseEvent(new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this); @@ -1082,7 +1082,7 @@ namespace Umbraco.Core.Services return Attempt.Fail( new OperationStatus(ex.Operation, evtMsgs)); } - uow.Commit(); + uow.Complete(); } MovedContentType.RaiseEvent(new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this); @@ -1128,7 +1128,7 @@ namespace Umbraco.Core.Services { return Attempt.Fail(new OperationStatus(null, ex.Operation, evtMsgs)); } - uow.Commit(); + uow.Complete(); } return Attempt.Succeed(new OperationStatus(copy, MoveOperationStatusType.Success, evtMsgs)); @@ -1171,7 +1171,7 @@ namespace Umbraco.Core.Services { return Attempt.Fail(new OperationStatus(null, ex.Operation, evtMsgs)); } - uow.Commit(); + uow.Complete(); } return Attempt.Succeed(new OperationStatus(copy, MoveOperationStatusType.Success, evtMsgs)); @@ -1195,7 +1195,7 @@ namespace Umbraco.Core.Services ValidateLocked(mediaType); // throws if invalid mediaType.CreatorId = userId; repository.AddOrUpdate(mediaType); - uow.Commit(); + uow.Complete(); } @@ -1235,7 +1235,7 @@ namespace Umbraco.Core.Services } //save it all in one go - uow.Commit(); + uow.Complete(); } UpdateContentXmlStructure(asArray.Cast().ToArray()); @@ -1264,7 +1264,7 @@ namespace Umbraco.Core.Services var repository = uow.CreateRepository(); repository.Delete(mediaType); - uow.Commit(); + uow.Complete(); DeletedMediaType.RaiseEvent(new DeleteEventArgs(mediaType, false), this); } @@ -1299,7 +1299,7 @@ namespace Umbraco.Core.Services { repository.Delete(mediaType); } - uow.Commit(); + uow.Complete(); DeletedMediaType.RaiseEvent(new DeleteEventArgs(asArray, false), this); } @@ -1361,7 +1361,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); repo.AddOrUpdate(new AuditItem(objectId, message, type, userId)); - uow.Commit(); + uow.Complete(); } } diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 2069fc45c6..b6dfa08727 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -52,7 +52,7 @@ namespace Umbraco.Core.Services } repo.AddOrUpdate(container); - uow.Commit(); + uow.Complete(); SavedContainer.RaiseEvent(new SaveEventArgs(container, evtMsgs), this); //TODO: Audit trail ? @@ -146,7 +146,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); repo.AddOrUpdate(container); - uow.Commit(); + uow.Complete(); } SavedContainer.RaiseEvent(new SaveEventArgs(container, evtMsgs), this); @@ -173,7 +173,7 @@ namespace Umbraco.Core.Services } repo.Delete(container); - uow.Commit(); + uow.Complete(); DeletedContainer.RaiseEvent(new DeleteEventArgs(container, evtMsgs), this); @@ -276,7 +276,7 @@ namespace Umbraco.Core.Services return list; } } - + /// /// Returns the PreValueCollection for the specified data type /// @@ -340,7 +340,7 @@ namespace Umbraco.Core.Services return Attempt.Fail( new OperationStatus(ex.Operation, evtMsgs)); } - uow.Commit(); + uow.Complete(); } Moved.RaiseEvent(new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this); @@ -356,19 +356,19 @@ namespace Umbraco.Core.Services /// Id of the user issueing the save public void Save(IDataTypeDefinition dataTypeDefinition, int userId = 0) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) return; + dataTypeDefinition.CreatorId = userId; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - dataTypeDefinition.CreatorId = userId; repository.AddOrUpdate(dataTypeDefinition); - uow.Commit(); - - Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); + uow.Complete(); } + Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } @@ -404,7 +404,7 @@ namespace Umbraco.Core.Services dataTypeDefinition.CreatorId = userId; repository.AddOrUpdate(dataTypeDefinition); } - uow.Commit(); + uow.Complete(); if (raiseEvents) Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinitions, false), this); @@ -484,7 +484,7 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.AddOrUpdatePreValues(dataTypeDefinition, values); - uow.Commit(); + uow.Complete(); } } @@ -499,23 +499,18 @@ namespace Umbraco.Core.Services if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) return; + dataTypeDefinition.CreatorId = userId; + using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - dataTypeDefinition.CreatorId = userId; - - //add/update the dtd - repository.AddOrUpdate(dataTypeDefinition); - - //add/update the prevalues - repository.AddOrUpdatePreValues(dataTypeDefinition, values); - - uow.Commit(); - - Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); + repository.AddOrUpdate(dataTypeDefinition); // definition + repository.AddOrUpdatePreValues(dataTypeDefinition, values); //prevalues + uow.Complete(); } - - Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); + + Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); + Audit(AuditType.Save, "Save DataTypeDefinition performed by user", userId, dataTypeDefinition.Id); } @@ -529,21 +524,20 @@ namespace Umbraco.Core.Services /// to delete /// Optional Id of the user issueing the deletion public void Delete(IDataTypeDefinition dataTypeDefinition, int userId = 0) - { - if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(dataTypeDefinition), this)) + { + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(dataTypeDefinition), this)) return; using (var uow = UowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); repository.Delete(dataTypeDefinition); - - uow.Commit(); - - Deleted.RaiseEvent(new DeleteEventArgs(dataTypeDefinition, false), this); + uow.Complete(); } - Audit(AuditType.Delete, string.Format("Delete DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); + Deleted.RaiseEvent(new DeleteEventArgs(dataTypeDefinition, false), this); + + Audit(AuditType.Delete, string.Format("Delete DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } private void Audit(AuditType type, string message, int userId, int objectId) @@ -552,7 +546,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); repo.AddOrUpdate(new AuditItem(objectId, message, type, userId)); - uow.Commit(); + uow.Complete(); } } diff --git a/src/Umbraco.Core/Services/DomainService.cs b/src/Umbraco.Core/Services/DomainService.cs index ba1dd81677..d954bf3835 100644 --- a/src/Umbraco.Core/Services/DomainService.cs +++ b/src/Umbraco.Core/Services/DomainService.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); return repo.Exists(domainName); - } + } } public Attempt Delete(IDomain domain) @@ -41,7 +41,7 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.Delete(domain); - uow.Commit(); + uow.Complete(); } var args = new DeleteEventArgs(domain, false, evtMsgs); @@ -99,7 +99,7 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.AddOrUpdate(domainEntity); - uow.Commit(); + uow.Complete(); } Saved.RaiseEvent(new SaveEventArgs(domainEntity, false, evtMsgs), this); @@ -109,14 +109,14 @@ namespace Umbraco.Core.Services #region Event Handlers /// /// Occurs before Delete - /// + /// public static event TypedEventHandler> Deleting; /// /// Occurs after Delete /// public static event TypedEventHandler> Deleted; - + /// /// Occurs before Save /// @@ -127,7 +127,7 @@ namespace Umbraco.Core.Services /// public static event TypedEventHandler> Saved; - + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ExternalLoginService.cs b/src/Umbraco.Core/Services/ExternalLoginService.cs index ce31eb734b..b860fe1131 100644 --- a/src/Umbraco.Core/Services/ExternalLoginService.cs +++ b/src/Umbraco.Core/Services/ExternalLoginService.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Services } /// - /// Returns all logins matching the login info - generally there should only be one but in some cases + /// Returns all logins matching the login info - generally there should only be one but in some cases /// there might be more than one depending on if an adminstrator has been editing/removing members /// /// @@ -56,7 +56,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); repo.SaveUserLogins(userId, logins); - uow.Commit(); + uow.Complete(); } } @@ -70,7 +70,7 @@ namespace Umbraco.Core.Services { var repo = uow.CreateRepository(); repo.DeleteUserLogins(userId); - uow.Commit(); + uow.Complete(); } } } diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index 35b94286ad..fa228e9dd0 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -31,14 +31,14 @@ namespace Umbraco.Core.Services private const string PartialViewMacroHeader = "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; public FileService( - IUnitOfWorkProvider fileProvider, - IDatabaseUnitOfWorkProvider dataProvider, + IUnitOfWorkProvider fileProvider, + IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(dataProvider, repositoryFactory, logger, eventMessagesFactory) { - _fileUowProvider = fileProvider; + _fileUowProvider = fileProvider; } @@ -85,12 +85,11 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.AddOrUpdate(stylesheet); - uow.Commit(); - - SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + uow.Complete(); } - Audit(AuditType.Save, string.Format("Save Stylesheet performed by user"), userId, -1); + SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + Audit(AuditType.Save, "Save Stylesheet performed by user", userId, -1); } /// @@ -100,22 +99,22 @@ namespace Umbraco.Core.Services /// public void DeleteStylesheet(string path, int userId = 0) { + Stylesheet stylesheet; using (var uow = _fileUowProvider.GetUnitOfWork()) { var repository = uow.CreateRepository(); - var stylesheet = repository.Get(path); + stylesheet = repository.Get(path); if (stylesheet == null) return; if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this)) return; repository.Delete(stylesheet); - uow.Commit(); - - DeletedStylesheet.RaiseEvent(new DeleteEventArgs(stylesheet, false), this); - - Audit(AuditType.Delete, string.Format("Delete Stylesheet performed by user"), userId, -1); + uow.Complete(); } + + DeletedStylesheet.RaiseEvent(new DeleteEventArgs(stylesheet, false), this); + Audit(AuditType.Delete, "Delete Stylesheet performed by user", userId, -1); } /// @@ -176,11 +175,10 @@ namespace Umbraco.Core.Services { var repository = uow.CreateRepository(); repository.AddOrUpdate(script); - uow.Commit(); - - SavedScript.RaiseEvent(new SaveEventArgs