From ac4de99f30c545f3128fe06ad04047aefdac3217 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Jan 2017 20:48:51 +1100 Subject: [PATCH] Initial commit of changing all IsRaisedEventCancelled to use the event manager and inside of a uow --- src/Umbraco.Core/CoreBootManager.cs | 2 +- .../Repositories/MemberGroupRepository.cs | 2 +- .../Repositories/RepositoryBase.cs | 4 +- .../Persistence/UnitOfWork/FileUnitOfWork.cs | 97 +--- .../UnitOfWork/FileUnitOfWorkProvider.cs | 23 +- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 78 +--- ...taPocoUnitOfWork.cs => ScopeUnitOfWork.cs} | 421 +++++++++--------- .../UnitOfWork/ScopeUnitOfWorkProvider.cs | 54 +++ src/Umbraco.Core/Services/ContentService.cs | 8 +- .../Services/ContentTypeService.cs | 70 +-- src/Umbraco.Core/Services/DataTypeService.cs | 111 ++--- src/Umbraco.Core/Services/FileService.cs | 68 +-- .../Services/LocalizationService.cs | 34 +- src/Umbraco.Core/Services/MacroService.cs | 38 +- src/Umbraco.Core/Services/MediaService.cs | 72 +-- .../Services/MemberGroupService.cs | 24 +- src/Umbraco.Core/Services/MemberService.cs | 56 +-- .../Services/MemberTypeService.cs | 38 +- src/Umbraco.Core/Services/RelationService.cs | 42 +- src/Umbraco.Core/Services/ServiceContext.cs | 6 +- src/Umbraco.Core/Services/UserService.cs | 55 ++- src/Umbraco.Core/Umbraco.Core.csproj | 3 +- .../Persistence/BaseTableByTableTest.cs | 9 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 2 +- .../TestHelpers/BaseUmbracoApplicationTest.cs | 6 +- src/Umbraco.Web/WebBootManager.cs | 2 +- 26 files changed, 642 insertions(+), 683 deletions(-) rename src/Umbraco.Core/Persistence/UnitOfWork/{PetaPocoUnitOfWork.cs => ScopeUnitOfWork.cs} (92%) create mode 100644 src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index bc8af45504..5b7da056b7 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -173,7 +173,7 @@ namespace Umbraco.Core return new ServiceContext( new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()), new PetaPocoUnitOfWorkProvider(scopeProvider), - new FileUnitOfWorkProvider(), + new FileUnitOfWorkProvider(scopeProvider), new PublishingStrategy(msgFactory, ProfilingLogger.Logger), ApplicationCache, ProfilingLogger.Logger, diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index 9a917b3ea4..e5eef9e659 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -284,7 +284,7 @@ namespace Umbraco.Core.Persistence.Repositories var missingRoles = roleNames.Except(existingRoles); var missingGroups = missingRoles.Select(x => new MemberGroup {Name = x}).ToArray(); - if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(missingGroups), this)) + if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(missingGroups), this, UnitOfWork.EventManager)) { return; } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index f0b512a6df..2623677e43 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -102,7 +102,7 @@ namespace Umbraco.Core.Persistence.Repositories { if (_isolatedCache != null) return _isolatedCache; - var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; // fixme cast! + var scope = ((ScopeUnitOfWork) UnitOfWork).Scope; // fixme cast! IsolatedRuntimeCache provider; switch (scope.RepositoryCacheMode) { @@ -164,7 +164,7 @@ namespace Umbraco.Core.Persistence.Repositories return _cachePolicy = NoRepositoryCachePolicy.Instance; _cachePolicy = CreateCachePolicy(IsolatedCache); - var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; // fixme cast! + var scope = ((ScopeUnitOfWork) UnitOfWork).Scope; // fixme cast! switch (scope.RepositoryCacheMode) { case RepositoryCacheMode.Default: diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs index c4c31849eb..1d9262384c 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs @@ -3,73 +3,26 @@ using System.Collections.Generic; using System.Linq; using System.Transactions; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; +using IsolationLevel = System.Data.IsolationLevel; namespace Umbraco.Core.Persistence.UnitOfWork { /// /// Represents the Unit of Work implementation for working with files /// - internal class FileUnitOfWork : IUnitOfWork + internal class FileUnitOfWork : ScopeUnitOfWork { private Guid _key; - private readonly Queue _operations = new Queue(); - public FileUnitOfWork() + public FileUnitOfWork(IScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified) : base(scopeProvider, isolationLevel) { _key = Guid.NewGuid(); } - #region Implementation of IUnitOfWork + #region Implementation of IUnitOfWork - /// - /// Registers an instance to be added through this - /// - /// The - /// The 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 - /// The 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 - /// The 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() + public override 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, @@ -81,9 +34,9 @@ namespace Umbraco.Core.Persistence.UnitOfWork // scope.Complete(); //} - while (_operations.Count > 0) + while (Operations.Count > 0) { - var operation = _operations.Dequeue(); + var operation = Operations.Dequeue(); switch (operation.Type) { case TransactionType.Insert: @@ -99,44 +52,16 @@ namespace Umbraco.Core.Persistence.UnitOfWork } // Clear everything - _operations.Clear(); + Operations.Clear(); _key = Guid.NewGuid(); } - public object Key + public override object Key { get { return _key; } } #endregion - - #region Operation - - /// - /// 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; } - } - - #endregion - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs index c27e86a515..f2c295adb5 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs @@ -1,17 +1,26 @@ -namespace Umbraco.Core.Persistence.UnitOfWork +using Umbraco.Core.Logging; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.UnitOfWork { /// /// Represents a Unit of Work Provider for creating a /// - public class FileUnitOfWorkProvider : IUnitOfWorkProvider + public class FileUnitOfWorkProvider : ScopeUnitOfWorkProvider { - #region Implementation of IUnitOfWorkProvider - - public IUnitOfWork GetUnitOfWork() + public FileUnitOfWorkProvider() + : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) { - return new FileUnitOfWork(); } - #endregion + public FileUnitOfWorkProvider(IScopeProvider scopeProvider) : base(scopeProvider) + { + } + + public override IScopeUnitOfWork GetUnitOfWork() + { + return new FileUnitOfWork(Provider); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 7bcfcba2b9..85e854e394 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -1,98 +1,44 @@ using System; -using System.Data; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.UnitOfWork { - - /// - /// Represents a Unit of Work Provider for creating a + /// Represents a Unit of Work Provider for creating a /// - public class PetaPocoUnitOfWorkProvider : IScopeUnitOfWorkProvider - { - private readonly IScopeProvider _scopeProvider; - + public class PetaPocoUnitOfWorkProvider : ScopeUnitOfWorkProvider + { [Obsolete("Use the constructor specifying an ILogger instead")] public PetaPocoUnitOfWorkProvider() - : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) - { - - } + : base(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) + { } [Obsolete("Use the constructor specifying an ILogger instead")] public PetaPocoUnitOfWorkProvider(string connectionString, string providerName) - : this(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, LoggerResolver.Current.Logger))) + : base(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, LoggerResolver.Current.Logger))) { } /// - /// Parameterless constructor uses defaults + /// Constructor /// public PetaPocoUnitOfWorkProvider(ILogger logger) - : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, logger))) - { - - } + : base(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, logger))) + { } /// - /// Constructor accepting custom connectino string and provider name + /// Constructor accepting custom connection string and provider name /// /// /// Connection String to use with Database /// Database Provider for the Connection String public PetaPocoUnitOfWorkProvider(ILogger logger, string connectionString, string providerName) - : this(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, logger))) + : base(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, logger))) { } - /// - /// Constructor accepting a instance - /// - /// - public PetaPocoUnitOfWorkProvider(IScopeProvider scopeProvider) + public PetaPocoUnitOfWorkProvider(IScopeProvider scopeProvider) : base(scopeProvider) { - Mandate.ParameterNotNull(scopeProvider, "scopeProvider"); - _scopeProvider = scopeProvider; } - - #region Implementation of IUnitOfWorkProvider - - //explicit implementation - IDatabaseUnitOfWork IDatabaseUnitOfWorkProvider.GetUnitOfWork() - { - return new PetaPocoUnitOfWork(_scopeProvider); - } - - /// - /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. - /// - /// - /// - /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from - /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database - /// and we Dispose of this Database object when the UOW is disposed. - /// - public IScopeUnitOfWork GetUnitOfWork() - { - return new PetaPocoUnitOfWork(_scopeProvider); - } - - /// - /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. - /// - /// - /// - /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from - /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database - /// and we Dispose of this Database object when the UOW is disposed. - /// - public IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) - { - return new PetaPocoUnitOfWork(_scopeProvider, isolationLevel); - } - - #endregion - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs similarity index 92% rename from src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs rename to src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs index 79d6357625..8bae95e5dd 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs @@ -1,209 +1,214 @@ -using System; -using System.Collections.Generic; -using System.Data; -using Umbraco.Core.Events; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Scoping; - -namespace Umbraco.Core.Persistence.UnitOfWork -{ - /// - /// Represents the Unit of Work implementation for PetaPoco - /// - internal class PetaPocoUnitOfWork : DisposableObject, IScopeUnitOfWork - { - private readonly Queue _operations = new Queue(); - private readonly IsolationLevel _isolationLevel; - private readonly IScopeProvider _scopeProvider; - private bool _completeScope = true; // scope is completed by default - private IScope _scope; - private Guid _key; - - /// - /// Used for testing - /// - internal Guid InstanceId { get; private set; } - - /// - /// Creates a new unit of work instance - /// - /// - /// - /// - /// This should normally not be used directly and should be created with the UnitOfWorkProvider - /// - internal PetaPocoUnitOfWork(IScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified) - { - _scopeProvider = scopeProvider; - _isolationLevel = isolationLevel; - _key = Guid.NewGuid(); - InstanceId = Guid.NewGuid(); - } - - /// - /// Registers an instance to be added through this - /// - /// The - /// The 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 - /// The 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 - /// The participating in the transaction - public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue( - new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Delete - }); - } - - /// - /// Commits all batched changes within the scope of a PetaPoco transaction - /// - /// - /// 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 within the scope of a PetaPoco transaction - /// - /// - /// Allows you to set a callback which is executed before the transaction is committed, allow you to add additional SQL - /// operations to the overall commit process after the queue has been processed. - /// - internal void Commit(Action transactionCompleting) - { - // this happens in a scope-managed transaction - - // in case anything goes wrong - _completeScope = false; - - 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; - } - } - - if (transactionCompleting != null) - transactionCompleting(Database); - - // all is ok - _completeScope = true; - - // Clear everything - _operations.Clear(); - _key = Guid.NewGuid(); - } - - public object Key - { - get { return _key; } - } - - public IScope Scope - { - get { return _scope ?? (_scope = _scopeProvider.CreateScope(_isolationLevel)); } - } - - public UmbracoDatabase Database - { - get { return Scope.Database; } - } - - public IEventManager EventManager - { - get { return Scope.Events; } - } - - #region Operation - - /// - /// 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; } - } - - #endregion - - /// - /// Ensures disposable objects are disposed - /// - /// - /// Ensures that the Transaction instance is disposed of - /// - protected override void DisposeResources() - { - _operations.Clear(); - if (_scope == null) return; - - if (_completeScope) _scope.Complete(); - _scope.Dispose(); - _scope = null; - } - - } +using System; +using System.Collections.Generic; +using System.Data; +using Umbraco.Core.Events; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.UnitOfWork +{ + /// + /// Represents the Unit of Work implementation for a Scope + /// + internal class ScopeUnitOfWork : DisposableObject, IScopeUnitOfWork + { + private readonly Queue _operations = new Queue(); + private readonly IsolationLevel _isolationLevel; + private readonly IScopeProvider _scopeProvider; + private bool _completeScope = true; // scope is completed by default + private IScope _scope; + private Guid _key; + + /// + /// Used for testing + /// + internal Guid InstanceId { get; private set; } + + /// + /// Creates a new unit of work instance + /// + /// + /// + /// + /// This should normally not be used directly and should be created with the UnitOfWorkProvider + /// + internal ScopeUnitOfWork(IScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified) + { + _scopeProvider = scopeProvider; + _isolationLevel = isolationLevel; + _key = Guid.NewGuid(); + InstanceId = Guid.NewGuid(); + } + + /// + /// Registers an instance to be added through this + /// + /// The + /// The 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 + /// The 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 + /// The participating in the transaction + public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository) + { + _operations.Enqueue( + new Operation + { + Entity = entity, + Repository = repository, + Type = TransactionType.Delete + }); + } + + /// + /// Commits all batched changes within the scope of a PetaPoco transaction + /// + /// + /// 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 virtual void Commit() + { + Commit(null); + } + + /// + /// Commits all batched changes within the scope of a PetaPoco transaction + /// + /// + /// Allows you to set a callback which is executed before the transaction is committed, allow you to add additional SQL + /// operations to the overall commit process after the queue has been processed. + /// + internal void Commit(Action transactionCompleting) + { + // this happens in a scope-managed transaction + + // in case anything goes wrong + _completeScope = false; + + 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; + } + } + + if (transactionCompleting != null) + transactionCompleting(Database); + + // all is ok + _completeScope = true; + + // Clear everything + _operations.Clear(); + _key = Guid.NewGuid(); + } + + public virtual object Key + { + get { return _key; } + } + + public IScope Scope + { + get { return _scope ?? (_scope = _scopeProvider.CreateScope(_isolationLevel)); } + } + + public UmbracoDatabase Database + { + get { return Scope.Database; } + } + + public IEventManager EventManager + { + get { return Scope.Events; } + } + + protected Queue Operations + { + get { return _operations; } + } + + #region Operation + + /// + /// Provides a snapshot of an entity and the repository reference it belongs to. + /// + protected 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; } + } + + #endregion + + /// + /// Ensures disposable objects are disposed + /// + /// + /// Ensures that the Transaction instance is disposed of + /// + protected override void DisposeResources() + { + _operations.Clear(); + if (_scope == null) return; + + if (_completeScope) _scope.Complete(); + _scope.Dispose(); + _scope = null; + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs new file mode 100644 index 0000000000..0b137e985a --- /dev/null +++ b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs @@ -0,0 +1,54 @@ +using System.Data; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.UnitOfWork +{ + public abstract class ScopeUnitOfWorkProvider : IScopeUnitOfWorkProvider + { + public IScopeProvider Provider { get; private set; } + + /// + /// Constructor accepting a instance + /// + /// + protected ScopeUnitOfWorkProvider(IScopeProvider scopeProvider) + { + Mandate.ParameterNotNull(scopeProvider, "scopeProvider"); + Provider = scopeProvider; + } + + //explicit implementation + IDatabaseUnitOfWork IDatabaseUnitOfWorkProvider.GetUnitOfWork() + { + return new ScopeUnitOfWork(Provider); + } + + /// + /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. + /// + /// + /// + /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from + /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database + /// and we Dispose of this Database object when the UOW is disposed. + /// + public virtual IScopeUnitOfWork GetUnitOfWork() + { + return new ScopeUnitOfWork(Provider); + } + + /// + /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. + /// + /// + /// + /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from + /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database + /// and we Dispose of this Database object when the UOW is disposed. + /// + public IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) + { + return new ScopeUnitOfWork(Provider, isolationLevel); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 343b4b66ad..25ea4da823 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -177,12 +177,8 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this, uow.EventManager); - using (var auditRepo = RepositoryFactory.CreateAuditRepository(uow)) - { - auditRepo.AddOrUpdate(new AuditItem(content.Id, string.Format("Content '{0}' was created", name), AuditType.New, content.CreatorId)); - uow.Commit(); - } - + Audit(AuditType.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id); + return content; } diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 31e86cd128..a8b1e7bc2c 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -719,12 +719,13 @@ namespace Umbraco.Core.Services /// Optional id of the user saving the ContentType public void Save(IContentType contentType, int userId = 0) { - if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(contentType), this)) - return; - using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(contentType), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { ValidateLocked(contentType); // throws if invalid @@ -749,12 +750,13 @@ namespace Umbraco.Core.Services { var asArray = contentTypes.ToArray(); - if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) - return; - using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { // all-or-nothing, validate them all first @@ -785,13 +787,14 @@ namespace Umbraco.Core.Services /// Optional id of the user issueing the delete /// Deleting a will delete all the objects based on this public void Delete(IContentType contentType, int userId = 0) - { - if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(contentType), this)) - return; - + { using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(contentType), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { //TODO: This needs to change, if we are deleting a content type, we should just delete the data, @@ -828,14 +831,15 @@ namespace Umbraco.Core.Services /// public void Delete(IEnumerable contentTypes, int userId = 0) { - var asArray = contentTypes.ToArray(); - - if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this)) - return; + var asArray = contentTypes.ToArray(); using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { var deletedContentTypes = new List(); @@ -1173,12 +1177,13 @@ namespace Umbraco.Core.Services /// Optional Id of the user saving the MediaType public void Save(IMediaType mediaType, int userId = 0) { - if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(mediaType), this)) - return; - using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(mediaType), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { ValidateLocked(mediaType); // throws if invalid @@ -1203,13 +1208,14 @@ namespace Umbraco.Core.Services public void Save(IEnumerable mediaTypes, int userId = 0) { var asArray = mediaTypes.ToArray(); - - if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) - return; - + using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { // all-or-nothing, validate them all first @@ -1241,14 +1247,16 @@ namespace Umbraco.Core.Services /// Optional Id of the user deleting the MediaType /// Deleting a will delete all the objects based on this public void Delete(IMediaType mediaType, int userId = 0) - { - if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(mediaType), this)) - return; + { using (new WriteLock(Locker)) { + var uow = UowProvider.GetUnitOfWork(); + + if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(mediaType), this, uow.EventManager)) + return; + _mediaService.DeleteMediaOfType(mediaType.Id, userId); - var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { var deletedMediaTypes = new List() {mediaType}; @@ -1273,17 +1281,19 @@ namespace Umbraco.Core.Services public void Delete(IEnumerable mediaTypes, int userId = 0) { var asArray = mediaTypes.ToArray(); - - if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this)) - return; + using (new WriteLock(Locker)) { + var uow = UowProvider.GetUnitOfWork(); + + if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this, uow.EventManager)) + return; + foreach (var mediaType in asArray) { _mediaService.DeleteMediaOfType(mediaType.Id); } - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { var deletedMediaTypes = new List(); diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 801869d078..3f42a441d8 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -360,11 +360,12 @@ namespace Umbraco.Core.Services /// to save /// Id of the user issueing the save public void Save(IDataTypeDefinition dataTypeDefinition, int userId = 0) - { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) - return; - + { var uow = UowProvider.GetUnitOfWork(); + + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) { dataTypeDefinition.CreatorId = userId; @@ -395,27 +396,29 @@ namespace Umbraco.Core.Services /// Boolean indicating whether or not to raise events public void Save(IEnumerable dataTypeDefinitions, int userId, bool raiseEvents) { - if (raiseEvents) + using (var uow = UowProvider.GetUnitOfWork()) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinitions), this)) - return; - } - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) - { - foreach (var dataTypeDefinition in dataTypeDefinitions) - { - dataTypeDefinition.CreatorId = userId; - repository.AddOrUpdate(dataTypeDefinition); - } - uow.Commit(); - if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinitions, false), this); - } + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinitions), this, uow.EventManager)) + return; + } - Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1); + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + { + foreach (var dataTypeDefinition in dataTypeDefinitions) + { + dataTypeDefinition.CreatorId = userId; + repository.AddOrUpdate(dataTypeDefinition); + } + uow.Commit(); + + if (raiseEvents) + Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinitions, false), this); + } + + Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1); + } } /// @@ -501,30 +504,32 @@ namespace Umbraco.Core.Services /// public void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary values, int userId = 0) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) - return; - - // if preValues contain the data type, override the data type definition accordingly - if (values != null && values.ContainsKey(Constants.PropertyEditors.PreValueKeys.DataValueType)) - dataTypeDefinition.DatabaseType = PropertyValueEditor.GetDatabaseType(values[Constants.PropertyEditors.PreValueKeys.DataValueType].Value); - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + using (var uow = UowProvider.GetUnitOfWork()) { - dataTypeDefinition.CreatorId = userId; + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this, uow.EventManager)) + return; - //add/update the dtd - repository.AddOrUpdate(dataTypeDefinition); + // if preValues contain the data type, override the data type definition accordingly + if (values != null && values.ContainsKey(Constants.PropertyEditors.PreValueKeys.DataValueType)) + dataTypeDefinition.DatabaseType = PropertyValueEditor.GetDatabaseType(values[Constants.PropertyEditors.PreValueKeys.DataValueType].Value); - //add/update the prevalues - repository.AddOrUpdatePreValues(dataTypeDefinition, values); + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + { + dataTypeDefinition.CreatorId = userId; - uow.Commit(); + //add/update the dtd + repository.AddOrUpdate(dataTypeDefinition); - Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); + //add/update the prevalues + repository.AddOrUpdatePreValues(dataTypeDefinition, values); + + uow.Commit(); + + Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); + } + + Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } - - Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } /// @@ -537,21 +542,23 @@ 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)) - return; - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) - { - repository.Delete(dataTypeDefinition); + { + using (var uow = UowProvider.GetUnitOfWork()) + { + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(dataTypeDefinition), this, uow.EventManager)) + return; - uow.Commit(); + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + { + repository.Delete(dataTypeDefinition); - Deleted.RaiseEvent(new DeleteEventArgs(dataTypeDefinition, false), this); - } + uow.Commit(); - 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); + } } /// diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index a8ad4b4885..8361541ff8 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -20,13 +20,13 @@ namespace Umbraco.Core.Services /// public class FileService : ScopeRepositoryService, IFileService { - private readonly IUnitOfWorkProvider _fileUowProvider; + private readonly IScopeUnitOfWorkProvider _fileUowProvider; private const string PartialViewHeader = "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"; private const string PartialViewMacroHeader = "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; public FileService( - IUnitOfWorkProvider fileProvider, + IScopeUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory, ILogger logger, @@ -71,19 +71,21 @@ namespace Umbraco.Core.Services /// public void SaveStylesheet(Stylesheet stylesheet, int userId = 0) { - if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this)) - return; - - var uow = _fileUowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateStylesheetRepository(uow, UowProvider.GetUnitOfWork())) + using (var uow = _fileUowProvider.GetUnitOfWork()) { - repository.AddOrUpdate(stylesheet); - uow.Commit(); + if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this, uow.EventManager)) + return; - SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + using (var repository = RepositoryFactory.CreateStylesheetRepository(uow, UowProvider.GetUnitOfWork())) + { + repository.AddOrUpdate(stylesheet); + uow.Commit(); + + SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + } + + Audit(AuditType.Save, string.Format("Save Stylesheet performed by user"), userId, -1); } - - Audit(AuditType.Save, string.Format("Save Stylesheet performed by user"), userId, -1); } /// @@ -99,7 +101,7 @@ namespace Umbraco.Core.Services var stylesheet = repository.Get(path); if (stylesheet == null) return; - if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this)) + if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this, uow.EventManager)) return; repository.Delete(stylesheet); @@ -161,12 +163,12 @@ namespace Umbraco.Core.Services /// public void SaveScript(Script script, int userId = 0) { - if (SavingScript.IsRaisedEventCancelled(new SaveEventArgs