diff --git a/src/Umbraco.Core/Services/ContentTypeContainerService.cs b/src/Umbraco.Core/Services/ContentTypeContainerService.cs index a1bcc4da30a7..a6ac5e811f6a 100644 --- a/src/Umbraco.Core/Services/ContentTypeContainerService.cs +++ b/src/Umbraco.Core/Services/ContentTypeContainerService.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services.Locking; namespace Umbraco.Cms.Core.Services; @@ -23,4 +24,8 @@ public ContentTypeContainerService( protected override Guid ContainedObjectType => Constants.ObjectTypes.DocumentType; protected override UmbracoObjectTypes ContainerObjectType => UmbracoObjectTypes.DocumentTypeContainer; + + protected override int[] ReadLockIds => ContentTypeLocks.ReadLockIds; + + protected override int[] WriteLockIds => ContentTypeLocks.WriteLockIds; } diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 6a5570d63212..f12daff4f2af 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Services.Locking; using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Core.Services; @@ -64,10 +65,9 @@ public ContentTypeService( StaticServiceProvider.Instance.GetRequiredService()) { } - // beware! order is important to avoid deadlocks - protected override int[] ReadLockIds { get; } = { Constants.Locks.ContentTypes }; + protected override int[] ReadLockIds => ContentTypeLocks.ReadLockIds; - protected override int[] WriteLockIds { get; } = { Constants.Locks.ContentTree, Constants.Locks.ContentTypes }; + protected override int[] WriteLockIds => ContentTypeLocks.WriteLockIds; protected override Guid ContainedObjectType => Constants.ObjectTypes.DocumentType; diff --git a/src/Umbraco.Core/Services/DataTypeContainerService.cs b/src/Umbraco.Core/Services/DataTypeContainerService.cs index 237aa00aec6f..0e6292f7f1fa 100644 --- a/src/Umbraco.Core/Services/DataTypeContainerService.cs +++ b/src/Umbraco.Core/Services/DataTypeContainerService.cs @@ -23,4 +23,10 @@ public DataTypeContainerService( protected override Guid ContainedObjectType => Constants.ObjectTypes.DataType; protected override UmbracoObjectTypes ContainerObjectType => UmbracoObjectTypes.DataTypeContainer; + + // data types do not have read/write locks (yet) + protected override int[] ReadLockIds => []; + + // data types do not have read/write locks (yet) + protected override int[] WriteLockIds => []; } diff --git a/src/Umbraco.Core/Services/EntityTypeContainerService.cs b/src/Umbraco.Core/Services/EntityTypeContainerService.cs index 9524d9b3db06..910b7babe392 100644 --- a/src/Umbraco.Core/Services/EntityTypeContainerService.cs +++ b/src/Umbraco.Core/Services/EntityTypeContainerService.cs @@ -22,6 +22,10 @@ internal abstract class EntityTypeContainerService GetAsync(Guid id) { using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true); + ReadLock(scope); return await Task.FromResult(_entityContainerRepository.Get(id)); } @@ -134,6 +139,7 @@ protected EntityTypeContainerService( public async Task> DeleteAsync(Guid id, Guid userKey) { using ICoreScope scope = ScopeProvider.CreateCoreScope(); + WriteLock(scope); EntityContainer? container = _entityContainerRepository.Get(id); if (container == null) @@ -178,6 +184,7 @@ protected EntityTypeContainerService( } using ICoreScope scope = ScopeProvider.CreateCoreScope(); + WriteLock(scope); EntityContainerOperationStatus operationValidationStatus = operationValidation(); if (operationValidationStatus != EntityContainerOperationStatus.Success) @@ -212,9 +219,26 @@ protected EntityTypeContainerService( } using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true); + ReadLock(scope); return _entityContainerRepository.Get(treeEntity.ParentId); } private void Audit(AuditType type, int userId, int objectId) => _auditRepository.Save(new AuditItem(objectId, type, userId, ContainerObjectType.GetName())); + + private void ReadLock(ICoreScope scope) + { + if (ReadLockIds.Any()) + { + scope.ReadLock(ReadLockIds); + } + } + + private void WriteLock(ICoreScope scope) + { + if (WriteLockIds.Any()) + { + scope.WriteLock(WriteLockIds); + } + } } diff --git a/src/Umbraco.Core/Services/Locking/ContentTypeLocks.cs b/src/Umbraco.Core/Services/Locking/ContentTypeLocks.cs new file mode 100644 index 000000000000..377d4f446d50 --- /dev/null +++ b/src/Umbraco.Core/Services/Locking/ContentTypeLocks.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Core.Services.Locking; + +internal static class ContentTypeLocks +{ + // beware! order is important to avoid deadlocks + internal static int[] ReadLockIds { get; } = { Constants.Locks.ContentTypes }; + + internal static int[] WriteLockIds { get; } = { Constants.Locks.ContentTree, Constants.Locks.ContentTypes }; +} diff --git a/src/Umbraco.Core/Services/Locking/MediaTypeLocks.cs b/src/Umbraco.Core/Services/Locking/MediaTypeLocks.cs new file mode 100644 index 000000000000..04456da3958f --- /dev/null +++ b/src/Umbraco.Core/Services/Locking/MediaTypeLocks.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Core.Services.Locking; + +internal static class MediaTypeLocks +{ + // beware! order is important to avoid deadlocks + internal static int[] ReadLockIds { get; } = { Constants.Locks.MediaTypes }; + + internal static int[] WriteLockIds { get; } = { Constants.Locks.MediaTree, Constants.Locks.MediaTypes }; +} diff --git a/src/Umbraco.Core/Services/MediaTypeContainerService.cs b/src/Umbraco.Core/Services/MediaTypeContainerService.cs index 28ae8ba2a999..6aeb9af4842a 100644 --- a/src/Umbraco.Core/Services/MediaTypeContainerService.cs +++ b/src/Umbraco.Core/Services/MediaTypeContainerService.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services.Locking; namespace Umbraco.Cms.Core.Services; @@ -23,4 +24,8 @@ public MediaTypeContainerService( protected override Guid ContainedObjectType => Constants.ObjectTypes.MediaType; protected override UmbracoObjectTypes ContainerObjectType => UmbracoObjectTypes.MediaTypeContainer; + + protected override int[] ReadLockIds => MediaTypeLocks.ReadLockIds; + + protected override int[] WriteLockIds => MediaTypeLocks.WriteLockIds; } diff --git a/src/Umbraco.Core/Services/MediaTypeService.cs b/src/Umbraco.Core/Services/MediaTypeService.cs index b7e59b1320f1..cc914e43bc66 100644 --- a/src/Umbraco.Core/Services/MediaTypeService.cs +++ b/src/Umbraco.Core/Services/MediaTypeService.cs @@ -7,6 +7,7 @@ using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Services.Locking; namespace Umbraco.Cms.Core.Services; @@ -60,10 +61,9 @@ public MediaTypeService( } - // beware! order is important to avoid deadlocks - protected override int[] ReadLockIds { get; } = { Constants.Locks.MediaTypes }; + protected override int[] ReadLockIds => MediaTypeLocks.ReadLockIds; - protected override int[] WriteLockIds { get; } = { Constants.Locks.MediaTree, Constants.Locks.MediaTypes }; + protected override int[] WriteLockIds => MediaTypeLocks.WriteLockIds; protected override Guid ContainedObjectType => Constants.ObjectTypes.MediaType;