using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; namespace Umbraco.Cms.Core.Services; /// /// Abstract base class for content-related services providing shared infrastructure. /// public abstract class ContentServiceBase : RepositoryService { protected readonly IDocumentRepository DocumentRepository; protected readonly IAuditService AuditService; protected readonly IUserIdKeyResolver UserIdKeyResolver; protected ContentServiceBase( ICoreScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IDocumentRepository documentRepository, IAuditService auditService, IUserIdKeyResolver userIdKeyResolver) : base(provider, loggerFactory, eventMessagesFactory) { DocumentRepository = documentRepository ?? throw new ArgumentNullException(nameof(documentRepository)); AuditService = auditService ?? throw new ArgumentNullException(nameof(auditService)); UserIdKeyResolver = userIdKeyResolver ?? throw new ArgumentNullException(nameof(userIdKeyResolver)); } /// /// Records an audit entry for a content operation (synchronous). /// /// /// Uses ConfigureAwait(false) to avoid capturing synchronization context and prevent deadlocks. /// TODO: Replace with sync overloads when IAuditService.Add and IUserIdKeyResolver.Get are available. /// protected void Audit(AuditType type, int userId, int objectId, string? message = null, string? parameters = null) { // Use ConfigureAwait(false) to avoid context capture and potential deadlocks Guid userKey = UserIdKeyResolver.GetAsync(userId).ConfigureAwait(false).GetAwaiter().GetResult(); AuditService.AddAsync( type, userKey, objectId, UmbracoObjectTypes.Document.GetName(), message, parameters).ConfigureAwait(false).GetAwaiter().GetResult(); } /// /// Records an audit entry for a content operation asynchronously. /// protected async Task AuditAsync(AuditType type, int userId, int objectId, string? message = null, string? parameters = null) { Guid userKey = await UserIdKeyResolver.GetAsync(userId).ConfigureAwait(false); await AuditService.AddAsync( type, userKey, objectId, UmbracoObjectTypes.Document.GetName(), message, parameters).ConfigureAwait(false); } }