diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 755fc5f04a..001705ebb9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -449,7 +449,15 @@ namespace Umbraco.Core.Persistence.Repositories foreach (var dto in dtos) { - yield return CreateContentFromDto(dto, dto.VersionId); + var fromCache = TryGetFromCache(dto.NodeId); + if (fromCache.Success) + { + yield return fromCache.Result; + } + else + { + yield return CreateContentFromDto(dto, dto.VersionId); + } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index cbd8b9d252..3967aef5e6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -85,11 +85,10 @@ namespace Umbraco.Core.Persistence.Repositories /// public TEntity Get(TId id) { - Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString()); - var rEntity = _cache.GetById(typeof(TEntity), key); - if (rEntity != null) + var fromCache = TryGetFromCache(id); + if (fromCache.Success) { - return (TEntity)rEntity; + return fromCache.Result; } var entity = PerformGet(id); @@ -112,6 +111,17 @@ namespace Umbraco.Core.Persistence.Repositories return entity; } + protected Attempt TryGetFromCache(TId id) + { + Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString()); + var rEntity = _cache.GetById(typeof(TEntity), key); + if (rEntity != null) + { + return new Attempt(true, (TEntity) rEntity); + } + return Attempt.False; + } + protected abstract IEnumerable PerformGetAll(params TId[] ids); /// /// Gets all entities of type TEntity or a list according to the passed in Ids @@ -173,14 +183,12 @@ namespace Umbraco.Core.Persistence.Repositories /// public bool Exists(TId id) { - Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString()); - var rEntity = _cache.GetById(typeof(TEntity), key); - if (rEntity != null) + var fromCache = TryGetFromCache(id); + if (fromCache.Success) { return true; } - - return PerformExists(id); + return PerformExists(id); } protected abstract int PerformCount(IQuery query); diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 83ff46d958..b47879bda1 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; @@ -250,6 +251,17 @@ namespace Umbraco.Core.Services } } + internal IEnumerable GetPublishedContentOfContentType(int id) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ContentTypeId == id); + var contents = repository.GetByPublishedVersion(query); + + return contents; + } + } + /// /// Gets a collection of objects by Level /// @@ -431,6 +443,19 @@ namespace Umbraco.Core.Services } } + /// + /// Gets all published content items + /// + /// + internal IEnumerable GetAllPublished() + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Trashed == false); + return repository.GetByPublishedVersion(query); + } + } + /// /// Gets a collection of objects, which has an expiration date less than or equal to today. /// @@ -1359,23 +1384,13 @@ namespace Umbraco.Core.Services uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)"); - - //get all content items that are published - // Consider creating a Path query instead of recursive method: - // var query = Query.Builder.Where(x => x.Path.StartsWith("-1")); - var rootContent = GetRootContent(); - foreach (var content in rootContent.Where(content => content.Published)) - { - list.Add(content); - list.AddRange(GetPublishedDescendants(content)); - } + + list.AddRange(GetAllPublished()); } else { foreach (var id in contentTypeIds) { - - //first we'll clear out the data from the cmsContentXml table for this type uow.Database.Execute(@"delete from cmsContentXml where nodeId in (select cmsDocument.nodeId from cmsDocument @@ -1383,7 +1398,7 @@ namespace Umbraco.Core.Services where published = 1 and contentType = @contentTypeId)", new {contentTypeId = id}); //now get all published content objects of this type and add to the list - list.AddRange(GetContentOfContentType(id).Where(content => content.Published)); + list.AddRange(GetPublishedContentOfContentType(id)); } } diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index 892cc6f84d..e49d2a1da3 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; @@ -66,6 +67,90 @@ namespace Umbraco.Tests.Services base.TearDown(); } + [Test] + public void Get_All_Published_Content() + { + var result = PrimeDbWithLotsOfContent(); + var contentSvc = (ContentService) ServiceContext.ContentService; + + var countOfPublished = result.Count(x => x.Published); + var contentTypeId = result.First().ContentTypeId; + + using (DisposableTimer.DebugDuration("Getting published content normally")) + { + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + + var published = new List(); + //get all content items that are published + var rootContent = contentSvc.GetRootContent(); + foreach (var content in rootContent.Where(content => content.Published)) + { + published.Add(content); + published.AddRange(contentSvc.GetPublishedDescendants(content)); + } + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + + } + + using (DisposableTimer.DebugDuration("Getting published content optimized")) + { + + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + + //get all content items that are published + var published = contentSvc.GetAllPublished(); + + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + } + } + + [Test] + public void Get_All_Published_Content_Of_Type() + { + var result = PrimeDbWithLotsOfContent(); + var contentSvc = (ContentService)ServiceContext.ContentService; + + var countOfPublished = result.Count(x => x.Published); + var contentTypeId = result.First().ContentTypeId; + + using (DisposableTimer.DebugDuration("Getting published content of type normally")) + { + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + //get all content items that are published of this type + var published = contentSvc.GetContentOfContentType(contentTypeId).Where(content => content.Published); + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + } + + using (DisposableTimer.DebugDuration("Getting published content of type optimized")) + { + + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + //get all content items that are published of this type + var published = contentSvc.GetPublishedContentOfContentType(contentTypeId); + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + } + } + [Test] public void Truncate_Insert_Vs_Update_Insert() { @@ -130,6 +215,51 @@ namespace Umbraco.Tests.Services } + private IEnumerable PrimeDbWithLotsOfContent() + { + var contentType1 = MockedContentTypes.CreateSimpleContentType(); + contentType1.AllowedAsRoot = true; + ServiceContext.ContentTypeService.Save(contentType1); + contentType1.AllowedContentTypes = new List + { + new ContentTypeSort + { + Alias = contentType1.Alias, + Id = new Lazy(() => contentType1.Id), + SortOrder = 0 + } + }; + var result = new List(); + ServiceContext.ContentTypeService.Save(contentType1); + IContent lastParent = MockedContent.CreateSimpleContent(contentType1); + ServiceContext.ContentService.SaveAndPublish(lastParent); + result.Add(lastParent); + //create 20 deep + for (var i = 0; i < 20; i++) + { + //for each level, create 20 + IContent content = null; + for (var j = 1; j <= 10; j++) + { + content = MockedContent.CreateSimpleContent(contentType1, "Name" + j, lastParent); + //only publish evens + if (j % 2 == 0) + { + ServiceContext.ContentService.SaveAndPublish(content); + } + else + { + ServiceContext.ContentService.Save(content); + } + result.Add(content); + } + + //assign the last one as the next parent + lastParent = content; + } + return result; + } + private IEnumerable PrimeDbWithLotsOfContentXmlRecords(Guid customObjectType) { var nodes = new List();