From 0cefdcbc20ab9c910a80f7db02431b7abd4c3267 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 27 Jan 2016 18:55:10 +0100 Subject: [PATCH] Fixes ContentTypeBaseRepository - there were several hack in place to query by GUID but this is totally irrelavent because we are caching them all so the result should be from the GetAll(), so this removes all of those hacks which greatly simplifies things. This fixes the MapContentTypes and MapMediaTypes methods - before this was looking up a collection and then recursing back to Get(int) (but we already have all of the data we need), then it was storing a lot of arrays in memory to build up an Entity when it's much faster to just iterate forward only and build up the entity. This saves 5 SQL calls on startup. --- src/Umbraco.Core/Models/ContentType.cs | 6 + .../Repositories/ContentTypeBaseRepository.cs | 531 ++++++++---------- .../Repositories/ContentTypeRepository.cs | 15 +- .../Repositories/MediaTypeRepository.cs | 22 +- .../Repositories/RepositoryBase.cs | 3 +- .../Querying/ContentTypeSqlMappingTests.cs | 12 +- 6 files changed, 265 insertions(+), 324 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index b6021e6538..0926e48e31 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -53,6 +53,9 @@ namespace Umbraco.Core.Models /// /// Gets or sets the alias of the default Template. + /// TODO: This should be ignored from cloning!!!!!!!!!!!!!! + /// - but to do that we have to implement callback hacks, this needs to be fixed in v8, + /// we should not store direct entity /// [IgnoreDataMember] public ITemplate DefaultTemplate @@ -79,6 +82,9 @@ namespace Umbraco.Core.Models /// /// Gets or Sets a list of Templates which are allowed for the ContentType + /// TODO: This should be ignored from cloning!!!!!!!!!!!!!! + /// - but to do that we have to implement callback hacks, this needs to be fixed in v8, + /// we should not store direct entity /// [DataMember] public IEnumerable AllowedTemplates diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index eb8dfde6b0..066c914bb4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -584,14 +584,13 @@ AND umbracoNode.id <> @id", } } - public static IEnumerable GetMediaTypes( - TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax, + public static IEnumerable GetMediaTypes( + Database db, ISqlSyntaxProvider sqlSyntax, TRepo contentTypeRepository) - where TRepo : IReadRepository - where TId: struct + where TRepo : IReadRepository { - IDictionary> allParentMediaTypeIds; - var mediaTypes = MapMediaTypes(mediaTypeIds, db, sqlSyntax, out allParentMediaTypeIds) + IDictionary> allParentMediaTypeIds; + var mediaTypes = MapMediaTypes(db, sqlSyntax, out allParentMediaTypeIds) .ToArray(); MapContentTypeChildren(mediaTypes, db, sqlSyntax, contentTypeRepository, allParentMediaTypeIds); @@ -599,16 +598,15 @@ AND umbracoNode.id <> @id", return mediaTypes; } - public static IEnumerable GetContentTypes( - TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax, + public static IEnumerable GetContentTypes( + Database db, ISqlSyntaxProvider sqlSyntax, TRepo contentTypeRepository, ITemplateRepository templateRepository) - where TRepo : IReadRepository - where TId : struct + where TRepo : IReadRepository { - IDictionary> allAssociatedTemplates; - IDictionary> allParentContentTypeIds; - var contentTypes = MapContentTypes(contentTypeIds, db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds) + IDictionary> allAssociatedTemplates; + IDictionary> allParentContentTypeIds; + var contentTypes = MapContentTypes(db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds) .ToArray(); if (contentTypes.Any()) @@ -623,12 +621,11 @@ AND umbracoNode.id <> @id", return contentTypes; } - internal static void MapContentTypeChildren(IContentTypeComposition[] contentTypes, + internal static void MapContentTypeChildren(IContentTypeComposition[] contentTypes, Database db, ISqlSyntaxProvider sqlSyntax, TRepo contentTypeRepository, - IDictionary> allParentContentTypeIds) - where TRepo : IReadRepository - where TId : struct + IDictionary> allParentContentTypeIds) + where TRepo : IReadRepository { //NOTE: SQL call #2 @@ -650,20 +647,20 @@ AND umbracoNode.id <> @id", var allParentIdsAsArray = allParentContentTypeIds.SelectMany(x => x.Value).Distinct().ToArray(); if (allParentIdsAsArray.Any()) { - var allParentContentTypes = contentTypeRepository.GetAll(allParentIdsAsArray).ToArray(); + //NO!!!!!!!!!!! Do not recurse lookup, we've already looked them all up + //var allParentContentTypes = contentTypeRepository.GetAll(allParentIdsAsArray).ToArray(); + + var allParentContentTypes = contentTypes.Where(x => allParentIdsAsArray.Contains(x.Id)).ToArray(); + foreach (var contentType in contentTypes) { - //TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids - // (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be - var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key; + var entityId = contentType.Id; var parentContentTypes = allParentContentTypes.Where(x => - { - //TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids - // (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be - var parentEntityId = typeof(TId) == typeof(int) ? x.Id : (object)x.Key; + { + var parentEntityId = x.Id; - return allParentContentTypeIds[(TId)entityId].Contains((TId)parentEntityId); + return allParentContentTypeIds[entityId].Contains(parentEntityId); }); foreach (var parentContentType in parentContentTypes) { @@ -681,13 +678,12 @@ AND umbracoNode.id <> @id", } - internal static void MapContentTypeTemplates(IContentType[] contentTypes, + internal static void MapContentTypeTemplates(IContentType[] contentTypes, Database db, TRepo contentTypeRepository, ITemplateRepository templateRepository, - IDictionary> associatedTemplates) - where TRepo : IReadRepository - where TId: struct + IDictionary> associatedTemplates) + where TRepo : IReadRepository { if (associatedTemplates == null || associatedTemplates.Any() == false) return; @@ -704,11 +700,9 @@ AND umbracoNode.id <> @id", foreach (var contentType in contentTypes) { - //TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids - // (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be - var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key; + var entityId = contentType.Id; - var associatedTemplateIds = associatedTemplates[(TId)entityId].Select(x => x.TemplateId) + var associatedTemplateIds = associatedTemplates[entityId].Select(x => x.TemplateId) .Distinct() .ToArray(); @@ -720,15 +714,10 @@ AND umbracoNode.id <> @id", } - internal static IEnumerable MapMediaTypes(TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax, - out IDictionary> parentMediaTypeIds) - where TId : struct - { - Mandate.That(mediaTypeIds.Any(), () => new InvalidOperationException("must be at least one content type id specified")); - Mandate.ParameterNotNull(db, "db"); - - //ensure they are unique - mediaTypeIds = mediaTypeIds.Distinct().ToArray(); + internal static IEnumerable MapMediaTypes(Database db, ISqlSyntaxProvider sqlSyntax, + out IDictionary> parentMediaTypeIds) + { + Mandate.ParameterNotNull(db, "db"); var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb, @@ -754,27 +743,10 @@ AND umbracoNode.id <> @id", ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @" ) ParentTypes ON ParentTypes.childContentTypeId = cmsContentType.nodeId - WHERE (umbracoNode.nodeObjectType = @nodeObjectType)"; - - if (mediaTypeIds.Any()) - { - //TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will - // work for the time being. - if (typeof(TId) == typeof(int)) - { - sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))"; - } - else if (typeof(TId) == typeof(Guid)) - { - sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))"; - } - } - - //NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count! - if ((mediaTypeIds.Length - 1) > 2000) - throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters"); - - var result = db.Fetch(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType), contentTypeIds = mediaTypeIds }); + WHERE (umbracoNode.nodeObjectType = @nodeObjectType) + ORDER BY ctId"; + + var result = db.Fetch(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType) }); if (result.Any() == false) { @@ -782,87 +754,109 @@ AND umbracoNode.id <> @id", return Enumerable.Empty(); } - parentMediaTypeIds = new Dictionary>(); + parentMediaTypeIds = new Dictionary>(); var mappedMediaTypes = new List(); - foreach (var contentTypeId in mediaTypeIds) + //loop through each result and fill in our required values, each row will contain different requried data than the rest. + // it is much quicker to iterate each result and populate instead of looking up the values over and over in the result like + // we used to do. + var queue = new Queue(result); + var currAllowedContentTypes = new List(); + while (queue.Count > 0) { - //the current content type id that we're working with - - var currentCtId = contentTypeId; - - //first we want to get the main content type data this is 1 : 1 with umbraco node data - - var ct = result - .Where(x => - { - //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is - // how it is for now. - return (typeof (TId) == typeof (int)) - ? x.ctId == currentCtId - : x.nUniqueId == currentCtId; - }) - .Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId }) - .DistinctBy(x => (int)x.ctId) - .FirstOrDefault(); - - if (ct == null) + var ct = queue.Dequeue(); + + //check for allowed content types + int? allowedCtId = ct.ctaAllowedId; + int? allowedCtSort = ct.ctaSortOrder; + string allowedCtAlias = ct.ctaAlias; + if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null) { - continue; + var ctSort = new ContentTypeSort(new Lazy(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias); + if (currAllowedContentTypes.Contains(ctSort) == false) + { + currAllowedContentTypes.Add(ctSort); + } } - var contentTypeDto = new ContentTypeDto + //always ensure there's a list for this content type + if (parentMediaTypeIds.ContainsKey(ct.ctId) == false) + parentMediaTypeIds[ct.ctId] = new List(); + + //check for parent ids and assign to the outgoing collection + int? parentId = ct.chtParentId; + if (parentId.HasValue) { - Alias = ct.ctAlias, - AllowAtRoot = ct.ctAllowAtRoot, - Description = ct.ctDesc, - Icon = ct.ctIcon, - IsContainer = ct.ctIsContainer, - NodeId = ct.ctId, - PrimaryKey = ct.ctPk, - Thumbnail = ct.ctThumb, - //map the underlying node dto - NodeDto = new NodeDto - { - CreateDate = ct.nCreateDate, - Level = (short)ct.nLevel, - NodeId = ct.ctId, - NodeObjectType = ct.nObjectType, - ParentId = ct.nParentId, - Path = ct.nPath, - SortOrder = ct.nSortOrder, - Text = ct.nName, - Trashed = ct.nTrashed, - UniqueId = ct.nUniqueId, - UserId = ct.nUser - } - }; + var associatedParentIds = parentMediaTypeIds[ct.ctId]; + if (associatedParentIds.Contains(parentId.Value) == false) + associatedParentIds.Add(parentId.Value); + } - //now create the media type object + if (queue.Count == 0 || queue.Peek().ctId != ct.ctId) + { + //it's the last in the queue or the content type is changing (moving to the next one) + var mediaType = CreateForMapping(ct, currAllowedContentTypes); + mappedMediaTypes.Add(mediaType); - var factory = new ContentTypeFactory(); - var mediaType = factory.BuildMediaTypeEntity(contentTypeDto); - - //map the allowed content types - //map the child content type ids - MapCommonContentTypeObjects(mediaType, currentCtId, result, parentMediaTypeIds); - - mappedMediaTypes.Add(mediaType); - } + //Here we need to reset the current variables, we're now collecting data for a different content type + currAllowedContentTypes = new List(); + } + } return mappedMediaTypes; } - internal static IEnumerable MapContentTypes(TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax, - out IDictionary> associatedTemplates, - out IDictionary> parentContentTypeIds) - where TId : struct + private static IMediaType CreateForMapping(dynamic currCt, List currAllowedContentTypes) + { + // * create the DTO object + // * create the content type object + // * map the allowed content types + // * add to the outgoing list + + var contentTypeDto = new ContentTypeDto + { + Alias = currCt.ctAlias, + AllowAtRoot = currCt.ctAllowAtRoot, + Description = currCt.ctDesc, + Icon = currCt.ctIcon, + IsContainer = currCt.ctIsContainer, + NodeId = currCt.ctId, + PrimaryKey = currCt.ctPk, + Thumbnail = currCt.ctThumb, + //map the underlying node dto + NodeDto = new NodeDto + { + CreateDate = currCt.nCreateDate, + Level = (short)currCt.nLevel, + NodeId = currCt.ctId, + NodeObjectType = currCt.nObjectType, + ParentId = currCt.nParentId, + Path = currCt.nPath, + SortOrder = currCt.nSortOrder, + Text = currCt.nName, + Trashed = currCt.nTrashed, + UniqueId = currCt.nUniqueId, + UserId = currCt.nUser + } + }; + + //now create the content type object + + var factory = new ContentTypeFactory(); + var mediaType = factory.BuildMediaTypeEntity(contentTypeDto); + + //map the allowed content types + mediaType.AllowedContentTypes = currAllowedContentTypes; + + return mediaType; + } + + internal static IEnumerable MapContentTypes(Database db, ISqlSyntaxProvider sqlSyntax, + out IDictionary> associatedTemplates, + out IDictionary> parentContentTypeIds) { Mandate.ParameterNotNull(db, "db"); - - //ensure they are unique - contentTypeIds = contentTypeIds.Distinct().ToArray(); - + var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId, cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb, @@ -897,28 +891,10 @@ AND umbracoNode.id <> @id", ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @" ) ParentTypes ON ParentTypes.childContentTypeId = cmsContentType.nodeId - WHERE (umbracoNode.nodeObjectType = @nodeObjectType)"; - - if (contentTypeIds.Any()) - { - //TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will - // work for the time being. - if (typeof(TId) == typeof(int)) - { - sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))"; - } - else if (typeof(TId) == typeof(Guid)) - { - sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))"; - } - } - - - //NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count! - if ((contentTypeIds.Length - 1) > 2000) - throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters"); - - var result = db.Fetch(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), contentTypeIds = contentTypeIds }); + WHERE (umbracoNode.nodeObjectType = @nodeObjectType) + ORDER BY ctId"; + + var result = db.Fetch(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType)}); if (result.Any() == false) { @@ -927,170 +903,139 @@ AND umbracoNode.id <> @id", return Enumerable.Empty(); } - parentContentTypeIds = new Dictionary>(); - associatedTemplates = new Dictionary>(); + parentContentTypeIds = new Dictionary>(); + associatedTemplates = new Dictionary>(); var mappedContentTypes = new List(); - foreach (var contentTypeId in contentTypeIds) + var queue = new Queue(result); + var currDefaultTemplate = -1; + var currAllowedContentTypes = new List(); + while (queue.Count > 0) { - //the current content type id that we're working with + var ct = queue.Dequeue(); - var currentCtId = contentTypeId; - - //first we want to get the main content type data this is 1 : 1 with umbraco node data - - var ct = result - .Where(x => - { - //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is - // how it is for now. - return (typeof(TId) == typeof(int)) - ? x.ctId == currentCtId - : x.nUniqueId == currentCtId; - }) - .Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId }) - .DistinctBy(x => (int)x.ctId) - .FirstOrDefault(); - - if (ct == null) + //check for default templates + bool? isDefaultTemplate = Convert.ToBoolean(ct.dtIsDefault); + int? templateId = ct.dtTemplateId; + if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && templateId.HasValue) { - continue; + currDefaultTemplate = templateId.Value; } - //get the unique list of associated templates - var defaultTemplates = result - .Where(x => - { - //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is - // how it is for now. - return (typeof(TId) == typeof(int)) - ? x.ctId == currentCtId - : x.nUniqueId == currentCtId; - }) - //use a tuple so that distinct checks both values (in some rare cases the dtIsDefault will not compute as bool?, so we force it with Convert.ToBoolean) - .Select(x => new Tuple(Convert.ToBoolean(x.dtIsDefault), x.dtTemplateId)) - .Where(x => x.Item1.HasValue && x.Item2.HasValue) - .Distinct() - .OrderByDescending(x => x.Item1.Value) - .ToArray(); - //if there isn't one set to default explicitly, we'll pick the first one - var defaultTemplate = defaultTemplates.FirstOrDefault(x => x.Item1.Value) - ?? defaultTemplates.FirstOrDefault(); + //always ensure there's a list for this content type + if (associatedTemplates.ContainsKey(ct.ctId) == false) + associatedTemplates[ct.ctId] = new List(); - var dtDto = new ContentTypeTemplateDto + //check for associated templates and assign to the outgoing collection + if (ct.tId != null) { - //create the content type dto - ContentTypeDto = new ContentTypeDto + var associatedTemplate = new AssociatedTemplate(ct.tId, ct.tAlias, ct.tText); + var associatedList = associatedTemplates[ct.ctId]; + + if (associatedList.Contains(associatedTemplate) == false) + associatedList.Add(associatedTemplate); + } + + //check for allowed content types + int? allowedCtId = ct.ctaAllowedId; + int? allowedCtSort = ct.ctaSortOrder; + string allowedCtAlias = ct.ctaAlias; + if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null) + { + var ctSort = new ContentTypeSort(new Lazy(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias); + if (currAllowedContentTypes.Contains(ctSort) == false) { - Alias = ct.ctAlias, - AllowAtRoot = ct.ctAllowAtRoot, - Description = ct.ctDesc, - Icon = ct.ctIcon, - IsContainer = ct.ctIsContainer, - NodeId = ct.ctId, - PrimaryKey = ct.ctPk, - Thumbnail = ct.ctThumb, - //map the underlying node dto - NodeDto = new NodeDto - { - CreateDate = ct.nCreateDate, - Level = (short)ct.nLevel, - NodeId = ct.ctId, - NodeObjectType = ct.nObjectType, - ParentId = ct.nParentId, - Path = ct.nPath, - SortOrder = ct.nSortOrder, - Text = ct.nName, - Trashed = ct.nTrashed, - UniqueId = ct.nUniqueId, - UserId = ct.nUser - } - }, - ContentTypeNodeId = ct.ctId, - IsDefault = defaultTemplate != null, - TemplateNodeId = defaultTemplate != null ? defaultTemplate.Item2.Value : 0, - }; + currAllowedContentTypes.Add(ctSort); + } + } - // We will map a subset of the associated template - alias, id, name + //always ensure there's a list for this content type + if (parentContentTypeIds.ContainsKey(ct.ctId) == false) + parentContentTypeIds[ct.ctId] = new List(); - associatedTemplates.Add(currentCtId, result - .Where(x => - { - //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is - // how it is for now. - return (typeof(TId) == typeof(int)) - ? x.ctId == currentCtId - : x.nUniqueId == currentCtId; - }) - .Where(x => x.tId != null) - .Select(x => new AssociatedTemplate(x.tId, x.tAlias, x.tText)) - .Distinct() - .ToArray()); + //check for parent ids and assign to the outgoing collection + int? parentId = ct.chtParentId; + if (parentId.HasValue) + { + var associatedParentIds = parentContentTypeIds[ct.ctId]; - //now create the content type object + if (associatedParentIds.Contains(parentId.Value) == false) + associatedParentIds.Add(parentId.Value); + } - var factory = new ContentTypeFactory(); - var contentType = factory.BuildContentTypeEntity(dtDto.ContentTypeDto); - - // NOTE - // that was done by the factory but makes little sense, moved here, so - // now we have to reset dirty props again (as the factory does it) and yet, - // we are not managing allowed templates... the whole thing is weird. - ((ContentType) contentType).DefaultTemplateId = dtDto.TemplateNodeId; - contentType.ResetDirtyProperties(false); + if (queue.Count == 0 || queue.Peek().ctId != ct.ctId) + { + //it's the last in the queue or the content type is changing (moving to the next one) + var contentType = CreateForMapping(ct, currAllowedContentTypes, currDefaultTemplate); + mappedContentTypes.Add(contentType); - //map the allowed content types - //map the child content type ids - MapCommonContentTypeObjects(contentType, currentCtId, result, parentContentTypeIds); - - mappedContentTypes.Add(contentType); + //Here we need to reset the current variables, we're now collecting data for a different content type + currDefaultTemplate = -1; + currAllowedContentTypes = new List(); + } } return mappedContentTypes; } - private static void MapCommonContentTypeObjects(T contentType, TId currentCtId, List result, IDictionary> parentContentTypeIds) - where T : IContentTypeBase - where TId : struct + private static IContentType CreateForMapping(dynamic currCt, List currAllowedContentTypes, int currDefaultTemplate) { - //map the allowed content types - contentType.AllowedContentTypes = result - .Where(x => - { - //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is - // how it is for now. - return (typeof(TId) == typeof(int)) - ? x.ctId == currentCtId - : x.nUniqueId == currentCtId; - }) - //use tuple so we can use distinct on all vals - .Select(x => new Tuple(x.ctaAllowedId, x.ctaSortOrder, x.ctaAlias)) - .Where(x => x.Item1.HasValue && x.Item2.HasValue && x.Item3 != null) - .Distinct() - .Select(x => new ContentTypeSort(new Lazy(() => x.Item1.Value), x.Item2.Value, x.Item3)) - .ToList(); + // * set the default template to the first one if a default isn't found + // * create the DTO object + // * create the content type object + // * map the allowed content types + // * add to the outgoing list - //map the child content type ids - parentContentTypeIds.Add(currentCtId, result - .Where(x => + var dtDto = new ContentTypeTemplateDto + { + //create the content type dto + ContentTypeDto = new ContentTypeDto { - //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is - // how it is for now. - return (typeof(TId) == typeof(int)) - ? x.ctId == currentCtId - : x.nUniqueId == currentCtId; - }) - .Select(x => - { - //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is - // how it is for now. - return (typeof(TId) == typeof(int)) - ? (TId?)x.chtParentId - : (TId?)x.chtParentKey; - }) - .Where(x => x.HasValue) - .Distinct() - .Select(x => x.Value).ToList()); + Alias = currCt.ctAlias, + AllowAtRoot = currCt.ctAllowAtRoot, + Description = currCt.ctDesc, + Icon = currCt.ctIcon, + IsContainer = currCt.ctIsContainer, + NodeId = currCt.ctId, + PrimaryKey = currCt.ctPk, + Thumbnail = currCt.ctThumb, + //map the underlying node dto + NodeDto = new NodeDto + { + CreateDate = currCt.nCreateDate, + Level = (short)currCt.nLevel, + NodeId = currCt.ctId, + NodeObjectType = currCt.nObjectType, + ParentId = currCt.nParentId, + Path = currCt.nPath, + SortOrder = currCt.nSortOrder, + Text = currCt.nName, + Trashed = currCt.nTrashed, + UniqueId = currCt.nUniqueId, + UserId = currCt.nUser + } + }, + ContentTypeNodeId = currCt.ctId, + IsDefault = currDefaultTemplate != -1, + TemplateNodeId = currDefaultTemplate != -1 ? currDefaultTemplate : 0, + }; + + //now create the content type object + + var factory = new ContentTypeFactory(); + var contentType = factory.BuildContentTypeEntity(dtDto.ContentTypeDto); + + // NOTE + // that was done by the factory but makes little sense, moved here, so + // now we have to reset dirty props again (as the factory does it) and yet, + // we are not managing allowed templates... the whole thing is weird. + ((ContentType)contentType).DefaultTemplateId = dtDto.TemplateNodeId; + contentType.ResetDirtyProperties(false); + + //map the allowed content types + contentType.AllowedContentTypes = currAllowedContentTypes; + + return contentType; } internal static void MapGroupsAndProperties(int[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax, diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index da6b0a6efb..910d5c41ea 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -51,14 +51,12 @@ namespace Umbraco.Core.Persistence.Repositories { if (ids.Any()) { - return ContentTypeQueryMapper.GetContentTypes(ids, Database, SqlSyntax, this, _templateRepository); - } - else - { - var sql = new Sql().Select("id").From(SqlSyntax).Where(dto => dto.NodeObjectType == NodeObjectTypeId); - var allIds = Database.Fetch(sql).ToArray(); - return ContentTypeQueryMapper.GetContentTypes(allIds, Database, SqlSyntax, this, _templateRepository); + //NOTE: This logic should never be executed according to our cache policy + return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository) + .Where(x => ids.Contains(x.Id)); } + + return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository); } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -300,9 +298,6 @@ namespace Umbraco.Core.Persistence.Repositories else { return GetAll(); - //var sql = new Sql().Select("id").From(SqlSyntax).Where(dto => dto.NodeObjectType == NodeObjectTypeId); - //var allIds = Database.Fetch(sql).ToArray(); - //return ContentTypeQueryMapper.GetContentTypes(allIds, Database, SqlSyntax, this, _templateRepository); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index 225aa759d1..bb6090951d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -40,25 +40,20 @@ namespace Umbraco.Core.Persistence.Repositories protected override IMediaType PerformGet(int id) { - var contentTypes = ContentTypeQueryMapper.GetMediaTypes( - new[] { id }, Database, SqlSyntax, this); - - var contentType = contentTypes.SingleOrDefault(); - return contentType; + //use the underlying GetAll which will force cache all content types + return GetAll().FirstOrDefault(x => x.Id == id); } protected override IEnumerable PerformGetAll(params int[] ids) { if (ids.Any()) { - return ContentTypeQueryMapper.GetMediaTypes(ids, Database, SqlSyntax, this); - } - else - { - var sql = new Sql().Select("id").From(SqlSyntax).Where(dto => dto.NodeObjectType == NodeObjectTypeId); - var allIds = Database.Fetch(sql).ToArray(); - return ContentTypeQueryMapper.GetMediaTypes(allIds, Database, SqlSyntax, this); + //NOTE: This logic should never be executed according to our cache policy + return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this) + .Where(x => ids.Contains(x.Id)); } + + return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this); } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -178,9 +173,6 @@ namespace Umbraco.Core.Persistence.Repositories else { return GetAll(); - //var sql = new Sql().Select("id").From(SqlSyntax).Where(dto => dto.NodeObjectType == NodeObjectTypeId); - //var allIds = Database.Fetch(sql).ToArray(); - //return ContentTypeQueryMapper.GetContentTypes(allIds, Database, SqlSyntax, this, _templateRepository); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index f6bcc46d06..88b8e93772 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -179,7 +179,8 @@ namespace Umbraco.Core.Persistence.Repositories using (var p = CachePolicyFactory.CreatePolicy()) { - return p.GetAll(ids, PerformGetAll); + var result = p.GetAll(ids, PerformGetAll); + return result; } } diff --git a/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs b/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs index caf872a995..68de27ba6d 100644 --- a/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs @@ -55,10 +55,11 @@ namespace Umbraco.Tests.Persistence.Querying transaction.Complete(); } - IDictionary> allAssociatedTemplates; - IDictionary> allParentContentTypeIds; + IDictionary> allAssociatedTemplates; + IDictionary> allParentContentTypeIds; var contentTypes = ContentTypeRepository.ContentTypeQueryMapper.MapContentTypes( - new[] {99997, 99998}, DatabaseContext.Database, SqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds) + DatabaseContext.Database, SqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds) + .Where(x => (new[] {99997, 99998}).Contains(x.Id)) .ToArray(); var contentType1 = contentTypes.SingleOrDefault(x => x.Id == 99997); @@ -109,9 +110,10 @@ namespace Umbraco.Tests.Persistence.Querying transaction.Complete(); } - IDictionary> allParentContentTypeIds; + IDictionary> allParentContentTypeIds; var contentTypes = ContentTypeRepository.ContentTypeQueryMapper.MapMediaTypes( - new[] { 99997, 99998 }, DatabaseContext.Database, SqlSyntax, out allParentContentTypeIds) + DatabaseContext.Database, SqlSyntax, out allParentContentTypeIds) + .Where(x => (new[] { 99997, 99998 }).Contains(x.Id)) .ToArray(); var contentType1 = contentTypes.SingleOrDefault(x => x.Id == 99997);