using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { public class EntityService : ScopeRepositoryService, IEntityService { private readonly IEntityRepository _entityRepository; private readonly Dictionary _objectTypes; private IQuery _queryRootEntity; private readonly IdkMap _idkMap; public EntityService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IdkMap idkMap, IEntityRepository entityRepository) : base(provider, logger, eventMessagesFactory) { _idkMap = idkMap; _entityRepository = entityRepository; _objectTypes = new Dictionary { { typeof (IDataType).FullName, UmbracoObjectTypes.DataType }, { typeof (IContent).FullName, UmbracoObjectTypes.Document }, { typeof (IContentType).FullName, UmbracoObjectTypes.DocumentType }, { typeof (IMedia).FullName, UmbracoObjectTypes.Media }, { typeof (IMediaType).FullName, UmbracoObjectTypes.MediaType }, { typeof (IMember).FullName, UmbracoObjectTypes.Member }, { typeof (IMemberType).FullName, UmbracoObjectTypes.MemberType }, }; } #region Static Queries // lazy-constructed because when the ctor runs, the query factory may not be ready private IQuery QueryRootEntity => _queryRootEntity ?? (_queryRootEntity = Query().Where(x => x.ParentId == -1)); #endregion // gets the object type, throws if not supported private UmbracoObjectTypes GetObjectType(Type type) { if (type?.FullName == null || !_objectTypes.TryGetValue(type.FullName, out var objType)) throw new NotSupportedException($"Type \"{type?.FullName ?? ""}\" is not supported here."); return objType; } /// public IEntitySlim Get(int id) { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Get(id); } } /// public IEntitySlim Get(Guid key) { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Get(key); } } /// public virtual IEntitySlim Get(int id, UmbracoObjectTypes objectType) { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Get(id, objectType.GetGuid()); } } /// public IEntitySlim Get(Guid key, UmbracoObjectTypes objectType) { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Get(key, objectType.GetGuid()); } } /// public virtual IEntitySlim Get(int id) where T : IUmbracoEntity { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Get(id); } } /// public virtual IEntitySlim Get(Guid key) where T : IUmbracoEntity { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Get(key); } } /// public bool Exists(int id) { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Exists(id); } } /// public bool Exists(Guid key) { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.Exists(key); } } /// public virtual IEnumerable GetAll() where T : IUmbracoEntity => GetAll(Array.Empty()); /// public virtual IEnumerable GetAll(params int[] ids) where T : IUmbracoEntity { var entityType = typeof (T); var objectType = GetObjectType(entityType); var objectTypeId = objectType.GetGuid(); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAll(objectTypeId, ids); } } /// public virtual IEnumerable GetAll(UmbracoObjectTypes objectType) => GetAll(objectType, Array.Empty()); /// public virtual IEnumerable GetAll(UmbracoObjectTypes objectType, params int[] ids) { var entityType = objectType.GetClrType(); if (entityType == null) throw new NotSupportedException($"Type \"{objectType}\" is not supported here."); GetObjectType(entityType); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAll(objectType.GetGuid(), ids); } } /// public virtual IEnumerable GetAll(Guid objectType) => GetAll(objectType, Array.Empty()); /// public virtual IEnumerable GetAll(Guid objectType, params int[] ids) { var entityType = ObjectTypes.GetClrType(objectType); GetObjectType(entityType); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAll(objectType, ids); } } /// public virtual IEnumerable GetAll(params Guid[] keys) where T : IUmbracoEntity { var entityType = typeof (T); var objectType = GetObjectType(entityType); var objectTypeId = objectType.GetGuid(); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAll(objectTypeId, keys); } } /// public IEnumerable GetAll(UmbracoObjectTypes objectType, Guid[] keys) { var entityType = objectType.GetClrType(); GetObjectType(entityType); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAll(objectType.GetGuid(), keys); } } /// public virtual IEnumerable GetAll(Guid objectType, params Guid[] keys) { var entityType = ObjectTypes.GetClrType(objectType); GetObjectType(entityType); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAll(objectType, keys); } } /// public virtual IEnumerable GetRootEntities(UmbracoObjectTypes objectType) { using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetByQuery(QueryRootEntity, objectType.GetGuid()); } } /// public virtual IEntitySlim GetParent(int id) { using (ScopeProvider.CreateScope(autoComplete: true)) { var entity = _entityRepository.Get(id); if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21) return null; return _entityRepository.Get(entity.ParentId); } } /// public virtual IEntitySlim GetParent(int id, UmbracoObjectTypes objectType) { using (ScopeProvider.CreateScope(autoComplete: true)) { var entity = _entityRepository.Get(id); if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21) return null; return _entityRepository.Get(entity.ParentId, objectType.GetGuid()); } } /// public virtual IEnumerable GetChildren(int parentId) { using (ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ParentId == parentId); return _entityRepository.GetByQuery(query); } } /// public virtual IEnumerable GetChildren(int parentId, UmbracoObjectTypes objectType) { using (ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ParentId == parentId); return _entityRepository.GetByQuery(query, objectType.GetGuid()); } } /// public virtual IEnumerable GetDescendants(int id) { using (ScopeProvider.CreateScope(autoComplete: true)) { var entity = _entityRepository.Get(id); var pathMatch = entity.Path + ","; var query = Query().Where(x => x.Path.StartsWith(pathMatch) && x.Id != id); return _entityRepository.GetByQuery(query); } } /// public virtual IEnumerable GetDescendants(int id, UmbracoObjectTypes objectType) { using (ScopeProvider.CreateScope(autoComplete: true)) { var entity = _entityRepository.Get(id); var query = Query().Where(x => x.Path.StartsWith(entity.Path) && x.Id != id); return _entityRepository.GetByQuery(query, objectType.GetGuid()); } } /// public IEnumerable GetPagedChildren(int id, UmbracoObjectTypes objectType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null) { using (ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ParentId == id && x.Trashed == false); return _entityRepository.GetPagedResultsByQuery(query, objectType.GetGuid(), pageIndex, pageSize, out totalRecords, filter, ordering); } } /// public IEnumerable GetPagedDescendants(int id, UmbracoObjectTypes objectType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null) { using (ScopeProvider.CreateScope(autoComplete: true)) { var objectTypeGuid = objectType.GetGuid(); var query = Query(); if (id != Constants.System.Root) { // lookup the path so we can use it in the prefix query below var paths = _entityRepository.GetAllPaths(objectTypeGuid, id).ToArray(); if (paths.Length == 0) { totalRecords = 0; return Enumerable.Empty(); } var path = paths[0].Path; query.Where(x => x.Path.SqlStartsWith(path + ",", TextColumnType.NVarchar)); } return _entityRepository.GetPagedResultsByQuery(query, objectTypeGuid, pageIndex, pageSize, out totalRecords, filter, ordering); } } /// public IEnumerable GetPagedDescendants(IEnumerable ids, UmbracoObjectTypes objectType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null) { totalRecords = 0; var idsA = ids.ToArray(); if (idsA.Length == 0) return Enumerable.Empty(); using (ScopeProvider.CreateScope(autoComplete: true)) { var objectTypeGuid = objectType.GetGuid(); var query = Query(); if (idsA.All(x => x != Constants.System.Root)) { var paths = _entityRepository.GetAllPaths(objectTypeGuid, idsA).ToArray(); if (paths.Length == 0) { totalRecords = 0; return Enumerable.Empty(); } var clauses = new List>>(); foreach (var id in idsA) { // if the id is root then don't add any clauses if (id == Constants.System.Root) continue; var entityPath = paths.FirstOrDefault(x => x.Id == id); if (entityPath == null) continue; var path = entityPath.Path; var qid = id; clauses.Add(x => x.Path.SqlStartsWith(path + ",", TextColumnType.NVarchar) || x.Path.SqlEndsWith("," + qid, TextColumnType.NVarchar)); } query.WhereAny(clauses); } return _entityRepository.GetPagedResultsByQuery(query, objectTypeGuid, pageIndex, pageSize, out totalRecords, filter, ordering); } } /// public IEnumerable GetPagedDescendants(UmbracoObjectTypes objectType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null, bool includeTrashed = true) { using (ScopeProvider.CreateScope(autoComplete: true)) { var query = Query(); if (includeTrashed == false) query.Where(x => x.Trashed == false); return _entityRepository.GetPagedResultsByQuery(query, objectType.GetGuid(), pageIndex, pageSize, out totalRecords, filter, ordering); } } /// public virtual UmbracoObjectTypes GetObjectType(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var sql = scope.SqlContext.Sql() .Select(x => x.NodeObjectType) .From() .Where(x => x.NodeId == id); var guid = scope.Database.ExecuteScalar(sql); return ObjectTypes.GetUmbracoObjectType(guid); } } /// public virtual UmbracoObjectTypes GetObjectType(Guid key) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var sql = scope.SqlContext.Sql() .Select(x => x.NodeObjectType) .From() .Where(x => x.UniqueId == key); var guid = scope.Database.ExecuteScalar(sql); return ObjectTypes.GetUmbracoObjectType(guid); } } /// public virtual UmbracoObjectTypes GetObjectType(IUmbracoEntity entity) { return entity is IEntitySlim light ? ObjectTypes.GetUmbracoObjectType(light.NodeObjectType) : GetObjectType(entity.Id); } /// public virtual Type GetEntityType(int id) { var objectType = GetObjectType(id); return objectType.GetClrType(); } /// public Attempt GetId(Guid key, UmbracoObjectTypes objectType) { return _idkMap.GetIdForKey(key, objectType); } /// public Attempt GetId(Udi udi) { return _idkMap.GetIdForUdi(udi); } /// public Attempt GetKey(int id, UmbracoObjectTypes umbracoObjectType) { return _idkMap.GetKeyForId(id, umbracoObjectType); } /// public virtual IEnumerable GetAllPaths(UmbracoObjectTypes objectType, params int[] ids) { var entityType = objectType.GetClrType(); GetObjectType(entityType); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAllPaths(objectType.GetGuid(), ids); } } /// public virtual IEnumerable GetAllPaths(UmbracoObjectTypes objectType, params Guid[] keys) { var entityType = objectType.GetClrType(); GetObjectType(entityType); using (ScopeProvider.CreateScope(autoComplete: true)) { return _entityRepository.GetAllPaths(objectType.GetGuid(), keys); } } /// public int ReserveId(Guid key) { NodeDto node; using (var scope = ScopeProvider.CreateScope()) { var sql = scope.SqlContext.Sql() .Select() .From() .Where(x => x.UniqueId == key && x.NodeObjectType == Constants.ObjectTypes.IdReservation); node = scope.Database.SingleOrDefault(sql); if (node != null) throw new InvalidOperationException("An identifier has already been reserved for this Udi."); node = new NodeDto { UniqueId = key, Text = "RESERVED.ID", NodeObjectType = Constants.ObjectTypes.IdReservation, CreateDate = DateTime.Now, UserId = null, ParentId = -1, Level = 1, Path = "-1", SortOrder = 0, Trashed = false }; scope.Database.Insert(node); scope.Complete(); } return node.NodeId; } } }