U4-6994 - more N+1 and refactoring
This commit is contained in:
@@ -1,22 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
@@ -81,7 +73,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
if (ids.Any())
|
||||
{
|
||||
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
|
||||
sql.Where("umbracoNode.id in (@ids)", new { ids });
|
||||
}
|
||||
|
||||
//we only want the newest ones with this method
|
||||
@@ -228,7 +220,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private void RebuildXmlStructuresProcessQuery(Func<IContent, XElement> serializer, IQuery<IContent> query, Transaction tr, int pageSize)
|
||||
{
|
||||
var pageIndex = 0;
|
||||
var total = long.MinValue;
|
||||
long total;
|
||||
var processed = 0;
|
||||
do
|
||||
{
|
||||
@@ -239,7 +231,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
// see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982
|
||||
var descendants = GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out total,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
ProcessQuery, "Path", Direction.Ascending, true);
|
||||
sql => ProcessQuery(sql), "Path", Direction.Ascending, true);
|
||||
|
||||
var xmlItems = (from descendant in descendants
|
||||
let xml = serializer(descendant)
|
||||
@@ -259,7 +251,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return GetAllBySql(sql);
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
public override IContent GetByVersion(Guid versionId)
|
||||
@@ -670,63 +662,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
|
||||
return GetAllBySql(sql);
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> GetAllBySql(Sql sql)
|
||||
{
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
|
||||
var content = new IContent[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
|
||||
// if the cache contains the published version, use it
|
||||
var cached = RuntimeCache.GetCacheItem<IContent>(GetCacheIdKey<IContent>(dto.NodeId));
|
||||
if (cached != null && cached.Published)
|
||||
{
|
||||
content[i] = cached;
|
||||
continue;
|
||||
}
|
||||
|
||||
// else, need to fetch the version from the database
|
||||
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
|
||||
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
var c = factory.BuildEntity(dto);
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
c.Template = _templateRepository.Get(dto.TemplateId.Value);
|
||||
content[i] = c;
|
||||
|
||||
defs.Add(new DocumentDefinition(
|
||||
dto.NodeId,
|
||||
dto.VersionId,
|
||||
dto.ContentVersionDto.VersionDate,
|
||||
dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
contentType
|
||||
));
|
||||
}
|
||||
|
||||
// going to load all properties for all docs from database,
|
||||
// but at least in one queries thus avoiding N+1
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
|
||||
var dtoIndex = 0;
|
||||
var defIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
if (defIndex == defs.Count) return content;
|
||||
while (dtoIndex < dtos.Count && dtos[dtoIndex].NodeId != defs[defIndex].Id) dtoIndex++;
|
||||
var cc = content[dtoIndex];
|
||||
cc.Properties = propertyData[cc.Id];
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)cc).ResetDirtyProperties(false);
|
||||
|
||||
defIndex++;
|
||||
}
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -902,7 +838,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
|
||||
|
||||
return GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
ProcessQuery, orderBy, orderDirection, orderBySystemField,
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
@@ -933,83 +869,79 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
|
||||
return base.GetDatabaseFieldNameForOrderBy(orderBy);
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sql)
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sql, bool withCache = false)
|
||||
{
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
|
||||
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
|
||||
|
||||
//nothing found
|
||||
if (dtos.Any() == false) return Enumerable.Empty<IContent>();
|
||||
var content = new IContent[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
var templateIds = new List<int>();
|
||||
|
||||
//content types
|
||||
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
|
||||
var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray())
|
||||
.ToArray();
|
||||
|
||||
|
||||
var ids = dtos
|
||||
.Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
.Select(x => x.TemplateId.Value).ToArray();
|
||||
|
||||
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
|
||||
var templates = ids.Length == 0 ? Enumerable.Empty<ITemplate>() : _templateRepository.GetAll(ids).ToArray();
|
||||
|
||||
var dtosWithContentTypes = dtos
|
||||
//This select into and null check are required because we don't have a foreign damn key on the contentType column
|
||||
// http://issues.umbraco.org/issue/U4-5503
|
||||
.Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) })
|
||||
.Where(x => x.contentType != null)
|
||||
.ToArray();
|
||||
|
||||
//Go get the property data for each document
|
||||
var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition(
|
||||
d.dto.NodeId,
|
||||
d.dto.VersionId,
|
||||
d.dto.ContentVersionDto.VersionDate,
|
||||
d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
d.contentType));
|
||||
|
||||
var propertyData = GetPropertyCollection(sql, docDefs);
|
||||
|
||||
return dtosWithContentTypes.Select(d => CreateContentFromDto(
|
||||
d.dto,
|
||||
contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId),
|
||||
templates.FirstOrDefault(tem => tem.Id == (d.dto.TemplateId.HasValue ? d.dto.TemplateId.Value : -1)),
|
||||
propertyData[d.dto.NodeId]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="template"></param>
|
||||
/// <param name="propCollection"></param>
|
||||
/// <returns></returns>
|
||||
private IContent CreateContentFromDto(DocumentDto dto,
|
||||
IContentType contentType,
|
||||
ITemplate template,
|
||||
Models.PropertyCollection propCollection)
|
||||
{
|
||||
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
var content = factory.BuildEntity(dto);
|
||||
|
||||
//Check if template id is set on DocumentDto, and get ITemplate if it is.
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
content.Template = template ?? _templateRepository.Get(dto.TemplateId.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure there isn't one set.
|
||||
content.Template = null;
|
||||
var dto = dtos[i];
|
||||
|
||||
// if the cache contains the published version, use it
|
||||
if (withCache)
|
||||
{
|
||||
var cached = RuntimeCache.GetCacheItem<IContent>(GetCacheIdKey<IContent>(dto.NodeId));
|
||||
if (cached != null && cached.Published)
|
||||
{
|
||||
content[i] = cached;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// else, need to fetch from the database
|
||||
// content type repository is full-cache so OK to get each one independently
|
||||
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
|
||||
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
content[i] = factory.BuildEntity(dto);
|
||||
|
||||
// need template
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
templateIds.Add(dto.TemplateId.Value);
|
||||
|
||||
// need properties
|
||||
defs.Add(new DocumentDefinition(
|
||||
dto.NodeId,
|
||||
dto.VersionId,
|
||||
dto.ContentVersionDto.VersionDate,
|
||||
dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
contentType
|
||||
));
|
||||
}
|
||||
|
||||
content.Properties = propCollection;
|
||||
// load all required templates in 1 query
|
||||
var templates = _templateRepository.GetAll(templateIds.ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
foreach (var def in defs)
|
||||
{
|
||||
// move to corresponding item (which has to exist)
|
||||
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
|
||||
|
||||
// complete the item
|
||||
var cc = content[dtoIndex];
|
||||
var dto = dtos[dtoIndex];
|
||||
ITemplate template = null;
|
||||
if (dto.TemplateId.HasValue)
|
||||
templates.TryGetValue(dto.TemplateId.Value, out template); // else null
|
||||
cc.Template = template;
|
||||
cc.Properties = propertyData[cc.Id];
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity) cc).ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)content).ResetDirtyProperties(false);
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,17 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
@@ -137,6 +133,74 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Overrides of VersionableRepositoryBase<IContent>
|
||||
|
||||
public override IEnumerable<IMedia> GetAllVersions(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
private IEnumerable<IMedia> ProcessQuery(Sql sql, bool withCache = false)
|
||||
{
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
var content = new IMedia[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
|
||||
// if the cache contains the item, use it
|
||||
if (withCache)
|
||||
{
|
||||
var cached = RuntimeCache.GetCacheItem<IMedia>(GetCacheIdKey<IMedia>(dto.NodeId));
|
||||
if (cached != null)
|
||||
{
|
||||
content[i] = cached;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// else, need to fetch from the database
|
||||
// content type repository is full-cache so OK to get each one independently
|
||||
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
|
||||
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
content[i] = factory.BuildEntity(dto);
|
||||
|
||||
// need properties
|
||||
defs.Add(new DocumentDefinition(
|
||||
dto.NodeId,
|
||||
dto.VersionId,
|
||||
dto.VersionDate,
|
||||
dto.ContentDto.NodeDto.CreateDate,
|
||||
contentType
|
||||
));
|
||||
}
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
foreach (var def in defs)
|
||||
{
|
||||
// move to corresponding item (which has to exist)
|
||||
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
|
||||
|
||||
// complete the item
|
||||
var cc = content[dtoIndex];
|
||||
cc.Properties = propertyData[cc.Id];
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity) cc).ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public override IMedia GetByVersion(Guid versionId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
@@ -175,7 +239,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var subQuery = new Sql()
|
||||
.Select("id")
|
||||
.From<NodeDto>(SqlSyntax)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
@@ -191,7 +255,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
Database.Execute(deleteSql);
|
||||
}
|
||||
|
||||
//now insert the data, again if something fails here, the whole transaction is reversed
|
||||
@@ -460,67 +524,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return GetPagedResultsByQuery<ContentVersionDto, Models.Media>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsContentVersion", "contentId"),
|
||||
ProcessQuery, orderBy, orderDirection, orderBySystemField,
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
|
||||
private IEnumerable<IMedia> ProcessQuery(Sql sql)
|
||||
{
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
|
||||
var ids = dtos.Select(x => x.ContentDto.ContentTypeId).ToArray();
|
||||
|
||||
//content types
|
||||
var contentTypes = ids.Length == 0 ? Enumerable.Empty<IMediaType>() : _mediaTypeRepository.GetAll(ids).ToArray();
|
||||
|
||||
var dtosWithContentTypes = dtos
|
||||
//This select into and null check are required because we don't have a foreign damn key on the contentType column
|
||||
// http://issues.umbraco.org/issue/U4-5503
|
||||
.Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentDto.ContentTypeId) })
|
||||
.Where(x => x.contentType != null)
|
||||
.ToArray();
|
||||
|
||||
//Go get the property data for each document
|
||||
var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition(
|
||||
d.dto.NodeId,
|
||||
d.dto.VersionId,
|
||||
d.dto.VersionDate,
|
||||
d.dto.ContentDto.NodeDto.CreateDate,
|
||||
d.contentType))
|
||||
.ToArray();
|
||||
|
||||
var propertyData = GetPropertyCollection(sql, docDefs);
|
||||
|
||||
return dtosWithContentTypes.Select(d => CreateMediaFromDto(
|
||||
d.dto,
|
||||
contentTypes.First(ct => ct.Id == d.dto.ContentDto.ContentTypeId),
|
||||
propertyData[d.dto.NodeId]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a media object from a ContentDto
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="propCollection"></param>
|
||||
/// <returns></returns>
|
||||
private IMedia CreateMediaFromDto(ContentVersionDto dto,
|
||||
IMediaType contentType,
|
||||
PropertyCollection propCollection)
|
||||
{
|
||||
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
var media = factory.BuildEntity(dto);
|
||||
|
||||
media.Properties = propCollection;
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)media).ResetDirtyProperties(false);
|
||||
return media;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a media object from a ContentDto
|
||||
/// </summary>
|
||||
|
||||
@@ -2,24 +2,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Relators;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Dynamics;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
@@ -380,6 +375,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Overrides of VersionableRepositoryBase<IMembershipUser>
|
||||
|
||||
public override IEnumerable<IMember> GetAllVersions(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
|
||||
{
|
||||
|
||||
@@ -645,7 +648,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return GetPagedResultsByQuery<MemberDto, Member>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsMember", "nodeId"),
|
||||
ProcessQuery, orderBy, orderDirection, orderBySystemField,
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
}
|
||||
|
||||
@@ -685,59 +688,65 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return base.GetEntityPropertyNameForOrderBy(orderBy);
|
||||
}
|
||||
|
||||
private IEnumerable<IMember> ProcessQuery(Sql sql)
|
||||
private IEnumerable<IMember> ProcessQuery(Sql sql, bool withCache = false)
|
||||
{
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
|
||||
var ids = dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray();
|
||||
var content = new IMember[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
|
||||
//content types
|
||||
var contentTypes = ids.Length == 0 ? Enumerable.Empty<IMemberType>() : _memberTypeRepository.GetAll(ids).ToArray();
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
|
||||
var dtosWithContentTypes = dtos
|
||||
//This select into and null check are required because we don't have a foreign damn key on the contentType column
|
||||
// http://issues.umbraco.org/issue/U4-5503
|
||||
.Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) })
|
||||
.Where(x => x.contentType != null)
|
||||
.ToArray();
|
||||
// if the cache contains the item, use it
|
||||
if (withCache)
|
||||
{
|
||||
var cached = RuntimeCache.GetCacheItem<IMember>(GetCacheIdKey<IMember>(dto.NodeId));
|
||||
if (cached != null)
|
||||
{
|
||||
content[i] = cached;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Go get the property data for each document
|
||||
IEnumerable<DocumentDefinition> docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition(
|
||||
d.dto.NodeId,
|
||||
d.dto.ContentVersionDto.VersionId,
|
||||
d.dto.ContentVersionDto.VersionDate,
|
||||
d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
d.contentType));
|
||||
// else, need to fetch from the database
|
||||
// content type repository is full-cache so OK to get each one independently
|
||||
var contentType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
|
||||
var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
content[i] = factory.BuildEntity(dto);
|
||||
|
||||
var propertyData = GetPropertyCollection(sql, docDefs);
|
||||
// need properties
|
||||
defs.Add(new DocumentDefinition(
|
||||
dto.NodeId,
|
||||
dto.ContentVersionDto.VersionId,
|
||||
dto.ContentVersionDto.VersionDate,
|
||||
dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
contentType
|
||||
));
|
||||
}
|
||||
|
||||
return dtosWithContentTypes.Select(d => CreateMemberFromDto(
|
||||
d.dto,
|
||||
contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId),
|
||||
propertyData[d.dto.NodeId]));
|
||||
}
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a member object from a MemberDto
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="propCollection"></param>
|
||||
/// <returns></returns>
|
||||
private IMember CreateMemberFromDto(MemberDto dto,
|
||||
IMemberType contentType,
|
||||
PropertyCollection propCollection)
|
||||
{
|
||||
var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.ContentVersionDto.NodeId);
|
||||
var member = factory.BuildEntity(dto);
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
foreach (var def in defs)
|
||||
{
|
||||
// move to corresponding item (which has to exist)
|
||||
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
|
||||
|
||||
member.Properties = propCollection;
|
||||
// complete the item
|
||||
var cc = content[dtoIndex];
|
||||
cc.Properties = propertyData[cc.Id];
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)member).ResetDirtyProperties(false);
|
||||
return member;
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)cc).ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user