using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Cache; using Umbraco.Core.CodeAnnotations; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { public class EntityService : ScopeRepositoryService, IEntityService { private readonly IRuntimeCacheProvider _runtimeCache; private readonly Dictionary>> _supportedObjectTypes; public EntityService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService, IContentTypeService contentTypeService, IMediaService mediaService, IDataTypeService dataTypeService, IMemberService memberService, IMemberTypeService memberTypeService, IRuntimeCacheProvider runtimeCache) : base(provider, repositoryFactory, logger, eventMessagesFactory) { _runtimeCache = runtimeCache; IContentTypeService contentTypeService1 = contentTypeService; _supportedObjectTypes = new Dictionary>> { {typeof (IDataTypeDefinition).FullName, new Tuple>(UmbracoObjectTypes.DataType, dataTypeService.GetDataTypeDefinitionById)}, {typeof (IContent).FullName, new Tuple>(UmbracoObjectTypes.Document, contentService.GetById)}, {typeof (IContentType).FullName, new Tuple>(UmbracoObjectTypes.DocumentType, contentTypeService1.GetContentType)}, {typeof (IMedia).FullName, new Tuple>(UmbracoObjectTypes.Media, mediaService.GetById)}, {typeof (IMediaType).FullName, new Tuple>(UmbracoObjectTypes.MediaType, contentTypeService1.GetMediaType)}, {typeof (IMember).FullName, new Tuple>(UmbracoObjectTypes.Member, memberService.GetById)}, {typeof (IMemberType).FullName, new Tuple>(UmbracoObjectTypes.MemberType, memberTypeService.Get)}, //{typeof (IUmbracoEntity).FullName, new Tuple>(UmbracoObjectTypes.EntityContainer, id => //{ // using (var uow = UowProvider.GetUnitOfWork()) // { // var found = uow.Database.FirstOrDefault("SELECT * FROM umbracoNode WHERE id=@id", new { id = id }); // return found == null ? null : new UmbracoEntity(found.Trashed) // { // Id = found.NodeId, // Name = found.Text, // Key = found.UniqueId, // SortOrder = found.SortOrder, // Path = found.Path, // NodeObjectTypeId = found.NodeObjectType.Value, // CreateDate = found.CreateDate, // CreatorId = found.UserId.Value, // Level = found.Level, // ParentId = found.ParentId // }; // } //})} }; } #region Static Queries private IQuery _rootEntityQuery; #endregion /// /// Returns the integer id for a given GUID /// /// /// /// public Attempt GetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType) { var result = _runtimeCache.GetCacheItem(CacheKeys.IdToKeyCacheKey + key, () => { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { switch (umbracoObjectType) { case UmbracoObjectTypes.Document: case UmbracoObjectTypes.MemberType: case UmbracoObjectTypes.Media: case UmbracoObjectTypes.Template: case UmbracoObjectTypes.MediaType: case UmbracoObjectTypes.DocumentType: case UmbracoObjectTypes.Member: case UmbracoObjectTypes.DataType: case UmbracoObjectTypes.DocumentTypeContainer: return uow.Database.ExecuteScalar(new Sql().Select("id").From().Where(dto => dto.UniqueId == key)); case UmbracoObjectTypes.RecycleBin: case UmbracoObjectTypes.Stylesheet: case UmbracoObjectTypes.MemberGroup: case UmbracoObjectTypes.ContentItem: case UmbracoObjectTypes.ContentItemType: case UmbracoObjectTypes.ROOT: case UmbracoObjectTypes.Unknown: default: throw new NotSupportedException(); } } }); return result.HasValue ? Attempt.Succeed(result.Value) : Attempt.Fail(); } /// /// Returns the GUID for a given integer id /// /// /// /// public Attempt GetKeyForId(int id, UmbracoObjectTypes umbracoObjectType) { var result = _runtimeCache.GetCacheItem(CacheKeys.KeyToIdCacheKey + id, () => { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { switch (umbracoObjectType) { case UmbracoObjectTypes.Document: case UmbracoObjectTypes.MemberType: case UmbracoObjectTypes.Media: case UmbracoObjectTypes.Template: case UmbracoObjectTypes.MediaType: case UmbracoObjectTypes.DocumentType: case UmbracoObjectTypes.Member: case UmbracoObjectTypes.DataType: return uow.Database.ExecuteScalar(new Sql().Select("uniqueID").From().Where(dto => dto.NodeId == id)); case UmbracoObjectTypes.RecycleBin: case UmbracoObjectTypes.Stylesheet: case UmbracoObjectTypes.MemberGroup: case UmbracoObjectTypes.ContentItem: case UmbracoObjectTypes.ContentItemType: case UmbracoObjectTypes.ROOT: case UmbracoObjectTypes.Unknown: default: throw new NotSupportedException("Unsupported object type (" + umbracoObjectType + ")."); } } }); return result.HasValue ? Attempt.Succeed(result.Value) : Attempt.Fail(); } public IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true) { if (loadBaseType) { using (var uow = UowProvider.GetUnitOfWork()) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.GetByKey(key); uow.Commit(); return ret; } } //SD: TODO: Need to enable this at some stage ... just need to ask Morten what the deal is with what this does. throw new NotSupportedException(); //var objectType = GetObjectType(key); //var entityType = GetEntityType(objectType); //var typeFullName = entityType.FullName; //var entity = _supportedObjectTypes[typeFullName].Item2(id); //return entity; } /// /// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph. /// /// /// By default this will load the base type with a minimum set of properties. /// /// Id of the object to retrieve /// Optional bool to load the complete object graph when set to False. /// An public virtual IUmbracoEntity Get(int id, bool loadBaseType = true) { if (loadBaseType) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.Get(id); return ret; } } var objectType = GetObjectType(id); var entityType = GetEntityType(objectType); var typeFullName = entityType.FullName; var entity = _supportedObjectTypes[typeFullName].Item2(id); return entity; } public IUmbracoEntity GetByKey(Guid key, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true) { if (loadBaseType) { var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.GetByKey(key, objectTypeId); return ret; } } //SD: TODO: Need to enable this at some stage ... just need to ask Morten what the deal is with what this does. throw new NotSupportedException(); //var entityType = GetEntityType(umbracoObjectType); //var typeFullName = entityType.FullName; //var entity = _supportedObjectTypes[typeFullName].Item2(id); //return entity; } /// /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. /// /// /// By default this will load the base type with a minimum set of properties. /// /// Id of the object to retrieve /// UmbracoObjectType of the entity to retrieve /// Optional bool to load the complete object graph when set to False. /// An public virtual IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true) { if (loadBaseType) { var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.Get(id, objectTypeId); return ret; } } var entityType = GetEntityType(umbracoObjectType); var typeFullName = entityType.FullName; var entity = _supportedObjectTypes[typeFullName].Item2(id); return entity; } public IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true) where T : IUmbracoEntity { throw new NotImplementedException(); } /// /// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph. /// /// /// By default this will load the base type with a minimum set of properties. /// /// Type of the model to retrieve. Must be based on an /// Id of the object to retrieve /// Optional bool to load the complete object graph when set to False. /// An public virtual IUmbracoEntity Get(int id, bool loadBaseType = true) where T : IUmbracoEntity { if (loadBaseType) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.Get(id); return ret; } } var typeFullName = typeof(T).FullName; Mandate.That(_supportedObjectTypes.ContainsKey(typeFullName), () => { throw new NotSupportedException ("The passed in type is not supported"); }); var entity = _supportedObjectTypes[typeFullName].Item2(id); return entity; } /// /// Gets the parent of entity by its id /// /// Id of the entity to retrieve the Parent for /// An public virtual IUmbracoEntity GetParent(int id) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var entity = repository.Get(id); if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21) return null; var ret = repository.Get(entity.ParentId); return ret; } } /// /// Gets the parent of entity by its id and UmbracoObjectType /// /// Id of the entity to retrieve the Parent for /// UmbracoObjectType of the parent to retrieve /// An public virtual IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var entity = repository.Get(id); if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21) return null; var objectTypeId = umbracoObjectType.GetGuid(); var ret = repository.Get(entity.ParentId, objectTypeId); return ret; } } /// /// Gets a collection of children by the parents Id /// /// Id of the parent to retrieve children for /// An enumerable list of objects public virtual IEnumerable GetChildren(int parentId) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var query = Query.Builder.Where(x => x.ParentId == parentId); var contents = repository.GetByQuery(query); return contents; } } /// /// Gets a collection of children by the parents Id and UmbracoObjectType /// /// Id of the parent to retrieve children for /// UmbracoObjectType of the children to retrieve /// An enumerable list of objects public virtual IEnumerable GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType) { var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var query = Query.Builder.Where(x => x.ParentId == parentId); var contents = repository.GetByQuery(query, objectTypeId); return contents; } } /// /// Returns a paged collection of children /// /// The parent id to return children for /// /// /// /// /// /// /// /// public IEnumerable GetPagedChildren(int parentId, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "") { var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var query = Query.Builder.Where(x => x.ParentId == parentId && x.Trashed == false); IQuery filterQuery = null; if (filter.IsNullOrWhiteSpace() == false) { filterQuery = Query.Builder.Where(x => x.Name.Contains(filter)); } var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery); return contents; } } /// /// Returns a paged collection of descendants /// /// /// /// /// /// /// /// /// /// public IEnumerable GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "") { var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var query = Query.Builder; //if the id is System Root, then just get all if (id != Constants.System.Root) query.Where(x => x.Path.SqlContains(string.Format(",{0},", id), TextColumnType.NVarchar)); IQuery filterQuery = null; if (filter.IsNullOrWhiteSpace() == false) { filterQuery = Query.Builder.Where(x => x.Name.Contains(filter)); } var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery); return contents; } } /// /// Returns a paged collection of descendants from the root /// /// /// /// /// /// /// /// /// true/false to include trashed objects /// public IEnumerable GetPagedDescendantsFromRoot(UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true) { var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var query = Query.Builder; //don't include trashed if specfied if (includeTrashed == false) { query.Where(x => x.Trashed == false); } IQuery filterQuery = null; if (filter.IsNullOrWhiteSpace() == false) { filterQuery = Query.Builder.Where(x => x.Name.Contains(filter)); } var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery); return contents; } } /// /// Gets a collection of descendents by the parents Id /// /// Id of entity to retrieve descendents for /// An enumerable list of objects public virtual IEnumerable GetDescendents(int id) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var entity = repository.Get(id); var pathMatch = entity.Path + ","; var query = Query.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != id); var entities = repository.GetByQuery(query); return entities; } } /// /// Gets a collection of descendents by the parents Id /// /// Id of entity to retrieve descendents for /// UmbracoObjectType of the descendents to retrieve /// An enumerable list of objects public virtual IEnumerable GetDescendents(int id, UmbracoObjectTypes umbracoObjectType) { var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var entity = repository.Get(id); var query = Query.Builder.Where(x => x.Path.StartsWith(entity.Path) && x.Id != id); var entities = repository.GetByQuery(query, objectTypeId); return entities; } } /// /// Gets a collection of the entities at the root, which corresponds to the entities with a Parent Id of -1. /// /// UmbracoObjectType of the root entities to retrieve /// An enumerable list of objects public virtual IEnumerable GetRootEntities(UmbracoObjectTypes umbracoObjectType) { //create it once if it is needed (no need for locking here) if (_rootEntityQuery == null) { _rootEntityQuery = Query.Builder.Where(x => x.ParentId == -1); } var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var entities = repository.GetByQuery(_rootEntityQuery, objectTypeId); return entities; } } /// /// Gets a collection of all of a given type. /// /// Type of the entities to retrieve /// An enumerable list of objects public virtual IEnumerable GetAll(params int[] ids) where T : IUmbracoEntity { var typeFullName = typeof(T).FullName; Mandate.That(_supportedObjectTypes.ContainsKey(typeFullName), () => { throw new NotSupportedException ("The passed in type is not supported"); }); var objectType = _supportedObjectTypes[typeFullName].Item1; return GetAll(objectType, ids); } /// /// Gets a collection of all of a given type. /// /// UmbracoObjectType of the entities to return /// /// An enumerable list of objects public virtual IEnumerable GetAll(UmbracoObjectTypes umbracoObjectType, params int[] ids) { var entityType = GetEntityType(umbracoObjectType); var typeFullName = entityType.FullName; Mandate.That(_supportedObjectTypes.ContainsKey(typeFullName), () => { throw new NotSupportedException ("The passed in type is not supported"); }); var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.GetAll(objectTypeId, ids); return ret; } } public IEnumerable GetAll(UmbracoObjectTypes umbracoObjectType, Guid[] keys) { var entityType = GetEntityType(umbracoObjectType); var typeFullName = entityType.FullName; Mandate.That(_supportedObjectTypes.ContainsKey(typeFullName), () => { throw new NotSupportedException ("The passed in type is not supported"); }); var objectTypeId = umbracoObjectType.GetGuid(); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.GetAll(objectTypeId, keys); return ret; } } /// /// Gets a collection of /// /// Guid id of the UmbracoObjectType /// /// An enumerable list of objects public virtual IEnumerable GetAll(Guid objectTypeId, params int[] ids) { var umbracoObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId); var entityType = GetEntityType(umbracoObjectType); var typeFullName = entityType.FullName; Mandate.That(_supportedObjectTypes.ContainsKey(typeFullName), () => { throw new NotSupportedException ("The passed in type is not supported"); }); using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var ret = repository.GetAll(objectTypeId, ids); return ret; } } /// /// Gets the UmbracoObjectType from the integer id of an IUmbracoEntity. /// /// Id of the entity /// public virtual UmbracoObjectTypes GetObjectType(int id) { using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) { var sql = new Sql().Select("nodeObjectType").From().Where(x => x.NodeId == id); var nodeObjectTypeId = uow.Database.ExecuteScalar(sql); var objectTypeId = nodeObjectTypeId; return UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId); } } /// /// Gets the UmbracoObjectType from the integer id of an IUmbracoEntity. /// /// Unique Id of the entity /// public virtual UmbracoObjectTypes GetObjectType(Guid key) { using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) { var sql = new Sql().Select("nodeObjectType").From().Where(x => x.UniqueId == key); var nodeObjectTypeId = uow.Database.ExecuteScalar(sql); var objectTypeId = nodeObjectTypeId; return UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId); } } /// /// Gets the UmbracoObjectType from an IUmbracoEntity. /// /// /// public virtual UmbracoObjectTypes GetObjectType(IUmbracoEntity entity) { var entityImpl = entity as UmbracoEntity; if (entityImpl == null) return GetObjectType(entity.Id); return UmbracoObjectTypesExtensions.GetUmbracoObjectType(entityImpl.NodeObjectTypeId); } /// /// Gets the Type of an entity by its Id /// /// Id of the entity /// Type of the entity public virtual Type GetEntityType(int id) { var objectType = GetObjectType(id); return GetEntityType(objectType); } /// /// Gets the Type of an entity by its /// /// /// Type of the entity public virtual Type GetEntityType(UmbracoObjectTypes umbracoObjectType) { var type = typeof(UmbracoObjectTypes); var memInfo = type.GetMember(umbracoObjectType.ToString()); var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute), false); var attribute = ((UmbracoObjectTypeAttribute)attributes[0]); if (attribute == null) throw new NullReferenceException("The passed in UmbracoObjectType does not contain an UmbracoObjectTypeAttribute, which is used to retrieve the Type."); if (attribute.ModelType == null) throw new NullReferenceException("The passed in UmbracoObjectType does not contain a Type definition"); return attribute.ModelType; } public bool Exists(Guid key) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var exists = repository.Exists(key); return exists; } } public bool Exists(int id) { using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) { var repository = RepositoryFactory.CreateEntityRepository(uow); var exists = repository.Exists(id); return exists; } } } }