Merge remote-tracking branch 'origin/dev-v7' into dev-v7.6
This commit is contained in:
@@ -27,15 +27,15 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#region Implementation of IEntityFactory<IContent,DocumentDto>
|
||||
|
||||
public IContent BuildEntity(DocumentDto dto)
|
||||
public static IContent BuildEntity(DocumentDto dto, IContentType contentType)
|
||||
{
|
||||
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType);
|
||||
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType);
|
||||
|
||||
try
|
||||
{
|
||||
content.DisableChangeTracking();
|
||||
|
||||
content.Id = _id;
|
||||
content.Id = dto.NodeId;
|
||||
content.Key = dto.ContentVersionDto.ContentDto.NodeDto.UniqueId;
|
||||
content.Name = dto.Text;
|
||||
content.NodeName = dto.ContentVersionDto.ContentDto.NodeDto.Text;
|
||||
@@ -49,8 +49,8 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
content.Published = dto.Published;
|
||||
content.CreateDate = dto.ContentVersionDto.ContentDto.NodeDto.CreateDate;
|
||||
content.UpdateDate = dto.ContentVersionDto.VersionDate;
|
||||
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?) null;
|
||||
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?) null;
|
||||
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null;
|
||||
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null;
|
||||
content.Version = dto.ContentVersionDto.VersionId;
|
||||
content.PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished;
|
||||
content.PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId;
|
||||
@@ -64,6 +64,13 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
content.EnableChangeTracking();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
|
||||
public IContent BuildEntity(DocumentDto dto)
|
||||
{
|
||||
return BuildEntity(dto, _contentType);
|
||||
}
|
||||
|
||||
public DocumentDto BuildDto(IContent entity)
|
||||
|
||||
@@ -27,15 +27,15 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
|
||||
|
||||
public IMedia BuildEntity(ContentVersionDto dto)
|
||||
public static IMedia BuildEntity(ContentVersionDto dto, IMediaType contentType)
|
||||
{
|
||||
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, _contentType);
|
||||
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, contentType);
|
||||
|
||||
try
|
||||
{
|
||||
media.DisableChangeTracking();
|
||||
|
||||
media.Id = _id;
|
||||
media.Id = dto.NodeId;
|
||||
media.Key = dto.ContentDto.NodeDto.UniqueId;
|
||||
media.Path = dto.ContentDto.NodeDto.Path;
|
||||
media.CreatorId = dto.ContentDto.NodeDto.UserId.Value;
|
||||
@@ -55,6 +55,13 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
media.EnableChangeTracking();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
|
||||
public IMedia BuildEntity(ContentVersionDto dto)
|
||||
{
|
||||
return BuildEntity(dto, _contentType);
|
||||
}
|
||||
|
||||
public ContentVersionDto BuildDto(IMedia entity)
|
||||
|
||||
@@ -30,11 +30,11 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
_updateDate = updateDate;
|
||||
}
|
||||
|
||||
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
|
||||
public static IEnumerable<Property> BuildEntity(IReadOnlyCollection<PropertyDataDto> dtos, PropertyType[] compositionTypeProperties, DateTime createDate, DateTime updateDate)
|
||||
{
|
||||
var properties = new List<Property>();
|
||||
|
||||
foreach (var propertyType in _compositionTypeProperties)
|
||||
foreach (var propertyType in compositionTypeProperties)
|
||||
{
|
||||
var propertyDataDto = dtos.LastOrDefault(x => x.PropertyTypeId == propertyType.Id);
|
||||
var property = propertyDataDto == null
|
||||
@@ -47,8 +47,8 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
property.DisableChangeTracking();
|
||||
|
||||
property.CreateDate = _createDate;
|
||||
property.UpdateDate = _updateDate;
|
||||
property.CreateDate = createDate;
|
||||
property.UpdateDate = updateDate;
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
property.ResetDirtyProperties(false);
|
||||
properties.Add(property);
|
||||
@@ -57,12 +57,18 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
property.EnableChangeTracking();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
[Obsolete("Use the static method instead, there's no reason to allocate one of these classes everytime we want to map values")]
|
||||
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
|
||||
{
|
||||
return BuildEntity(dtos, _compositionTypeProperties, _createDate, _updateDate);
|
||||
}
|
||||
|
||||
public IEnumerable<PropertyDataDto> BuildDto(IEnumerable<Property> properties)
|
||||
{
|
||||
var propertyDataDtos = new List<PropertyDataDto>();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal enum BaseQueryType
|
||||
{
|
||||
Full,
|
||||
Ids,
|
||||
Count
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.Where<DocumentDto>(x => x.Newest)
|
||||
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
@@ -70,63 +70,90 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IEnumerable<IContent> PerformGetAll(params int[] ids)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
if (ids.Any())
|
||||
Func<Sql, Sql> translate = s =>
|
||||
{
|
||||
sql.Where("umbracoNode.id in (@ids)", new { ids });
|
||||
}
|
||||
if (ids.Any())
|
||||
{
|
||||
s.Where("umbracoNode.id in (@ids)", new { ids });
|
||||
}
|
||||
//we only want the newest ones with this method
|
||||
s.Where<DocumentDto>(x => x.Newest, SqlSyntax);
|
||||
return s;
|
||||
};
|
||||
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
|
||||
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
|
||||
//we only want the newest ones with this method
|
||||
sql.Where<DocumentDto>(x => x.Newest);
|
||||
|
||||
return ProcessQuery(sql);
|
||||
return ProcessQuery(translate(sqlBaseFull), translate(sqlBaseIds));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
|
||||
{
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IContent>(sqlClause, query);
|
||||
var sql = translator.Translate()
|
||||
.Where<DocumentDto>(x => x.Newest)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
|
||||
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
|
||||
return ProcessQuery(sql);
|
||||
Func<SqlTranslator<IContent>, Sql> translate = (translator) =>
|
||||
{
|
||||
return translator.Translate()
|
||||
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
};
|
||||
|
||||
var translatorFull = new SqlTranslator<IContent>(sqlBaseFull, query);
|
||||
var translatorIds = new SqlTranslator<IContent>(sqlBaseIds, query);
|
||||
|
||||
return ProcessQuery(translate(translatorFull), translate(translatorIds));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<IContent>
|
||||
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
protected override Sql GetBaseQuery(BaseQueryType queryType)
|
||||
{
|
||||
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
|
||||
var sql = new Sql();
|
||||
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsDocument.nodeId" : "*"))
|
||||
.From<DocumentDto>(SqlSyntax)
|
||||
.InnerJoin<ContentVersionDto>(SqlSyntax)
|
||||
.On<DocumentDto, ContentVersionDto>(SqlSyntax, left => left.VersionId, right => right.VersionId)
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
|
||||
|
||||
if (queryType == BaseQueryType.Full)
|
||||
{
|
||||
//The only reason we apply this left outer join is to be able to pull back the DocumentPublishedReadOnlyDto
|
||||
//information with the entire data set, so basically this will get both the latest document and also it's published
|
||||
//version if it has one. When performing a count or when just retrieving Ids like in paging, this is unecessary
|
||||
//and causes huge performance overhead for the SQL server, especially when sorting the result.
|
||||
//To fix this perf overhead we'd need another index on :
|
||||
// CREATE NON CLUSTERED INDEX ON cmsDocument.node + cmsDocument.published
|
||||
|
||||
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
|
||||
SqlSyntax.GetQuotedTableName("cmsDocument"),
|
||||
SqlSyntax.GetQuotedTableName("cmsDocument2"),
|
||||
SqlSyntax.GetQuotedColumnName("nodeId"),
|
||||
SqlSyntax.GetQuotedColumnName("published"));
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select(isCount ? "COUNT(*)" : "*")
|
||||
.From<DocumentDto>()
|
||||
.InnerJoin<ContentVersionDto>()
|
||||
.On<DocumentDto, ContentVersionDto>(left => left.VersionId, right => right.VersionId)
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
|
||||
// cannot do this because PetaPoco does not know how to alias the table
|
||||
//.LeftOuterJoin<DocumentPublishedReadOnlyDto>()
|
||||
//.On<DocumentDto, DocumentPublishedReadOnlyDto>(left => left.NodeId, right => right.NodeId)
|
||||
// so have to rely on writing our own SQL
|
||||
.Append(sqlx/*, new { @published = true }*/)
|
||||
sql.Append(sqlx /*, new { @published = true }*/);
|
||||
}
|
||||
|
||||
sql.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
|
||||
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
return "umbracoNode.id = @Id";
|
||||
@@ -173,20 +200,32 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
// not bring much safety - so this reverts to updating each record individually,
|
||||
// and it may be slower in the end, but should be more resilient.
|
||||
|
||||
var baseId = 0;
|
||||
var contentTypeIdsA = contentTypeIds == null ? new int[0] : contentTypeIds.ToArray();
|
||||
|
||||
Func<int, Sql, Sql> translate = (bId, sql) =>
|
||||
{
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
{
|
||||
sql.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
|
||||
}
|
||||
|
||||
sql
|
||||
.Where<NodeDto>(x => x.NodeId > bId && x.Trashed == false, SqlSyntax)
|
||||
.Where<DocumentDto>(x => x.Published, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
|
||||
return sql;
|
||||
};
|
||||
|
||||
var baseId = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// get the next group of nodes
|
||||
var query = GetBaseQuery(false);
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
query = query
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
|
||||
query = query
|
||||
.Where<NodeDto>(x => x.NodeId > baseId && x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
|
||||
var sqlFull = translate(baseId, GetBaseQuery(BaseQueryType.Full));
|
||||
var sqlIds = translate(baseId, GetBaseQuery(BaseQueryType.Ids));
|
||||
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(sqlFull, groupSize), SqlSyntax.SelectTop(sqlIds, groupSize))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
.ToList();
|
||||
|
||||
@@ -212,17 +251,23 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public override IEnumerable<IContent> GetAllVersions(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
Func<Sql, Sql> translate = s =>
|
||||
{
|
||||
return s.Where(GetBaseWhereClause(), new {Id = id})
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
};
|
||||
|
||||
var sqlFull = translate(GetBaseQuery(BaseQueryType.Full));
|
||||
var sqlIds = translate(GetBaseQuery(BaseQueryType.Ids));
|
||||
|
||||
return ProcessQuery(sqlFull, sqlIds, true);
|
||||
}
|
||||
|
||||
public override IContent GetByVersion(Guid versionId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql).FirstOrDefault();
|
||||
|
||||
@@ -238,10 +283,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<DocumentDto>()
|
||||
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
|
||||
.Where<ContentVersionDto>(x => x.VersionId == versionId)
|
||||
.Where<DocumentDto>(x => x.Newest != true);
|
||||
.From<DocumentDto>(SqlSyntax)
|
||||
.InnerJoin<ContentVersionDto>(SqlSyntax).On<ContentVersionDto, DocumentDto>(SqlSyntax, left => left.VersionId, right => right.VersionId)
|
||||
.Where<ContentVersionDto>(x => x.VersionId == versionId, SqlSyntax)
|
||||
.Where<DocumentDto>(x => x.Newest != true, SqlSyntax);
|
||||
var dto = Database.Fetch<DocumentDto, ContentVersionDto>(sql).FirstOrDefault();
|
||||
|
||||
if (dto == null) return;
|
||||
@@ -617,19 +662,25 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IEnumerable<IContent> GetByPublishedVersion(IQuery<IContent> query)
|
||||
{
|
||||
Func<SqlTranslator<IContent>, Sql> translate = t =>
|
||||
{
|
||||
return t.Translate()
|
||||
.Where<DocumentDto>(x => x.Published, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
};
|
||||
|
||||
// we WANT to return contents in top-down order, ie parents should come before children
|
||||
// ideal would be pure xml "document order" which can be achieved with:
|
||||
// ORDER BY substring(path, 1, len(path) - charindex(',', reverse(path))), sortOrder
|
||||
// but that's probably an overkill - sorting by level,sortOrder should be enough
|
||||
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IContent>(sqlClause, query);
|
||||
var sql = translator.Translate()
|
||||
.Where<DocumentDto>(x => x.Published, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
var sqlFull = GetBaseQuery(BaseQueryType.Full);
|
||||
var translatorFull = new SqlTranslator<IContent>(sqlFull, query);
|
||||
var sqlIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
var translatorIds = new SqlTranslator<IContent>(sqlIds, query);
|
||||
|
||||
return ProcessQuery(sql, true);
|
||||
return ProcessQuery(translate(translatorFull), translate(translatorIds), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -807,9 +858,9 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
Func<Tuple<string, object[]>> filterCallback = () => new Tuple<string, object[]>(filterSql.SQL, filterSql.Arguments);
|
||||
|
||||
return GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out totalRecords,
|
||||
return GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
(sqlFull, sqlIds) => ProcessQuery(sqlFull, sqlIds), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
@@ -840,16 +891,32 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
return base.GetDatabaseFieldNameForOrderBy(orderBy);
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sql, bool withCache = false)
|
||||
/// <summary>
|
||||
/// This is the underlying method that processes most queries for this repository
|
||||
/// </summary>
|
||||
/// <param name="sqlFull">
|
||||
/// The full SQL with the outer join to return all data required to create an IContent
|
||||
/// </param>
|
||||
/// <param name="sqlIds">
|
||||
/// The Id SQL without the outer join to just return all document ids - used to process the properties for the content item
|
||||
/// </param>
|
||||
/// <param name="withCache"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sqlFull, Sql sqlIds, bool withCache = false)
|
||||
{
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sqlFull);
|
||||
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
|
||||
|
||||
var content = new IContent[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
var templateIds = new List<int>();
|
||||
|
||||
|
||||
//track the looked up content types, even though the content types are cached
|
||||
// they still need to be deep cloned out of the cache and we don't want to add
|
||||
// the overhead of deep cloning them on every item in this loop
|
||||
var contentTypes = new Dictionary<int, IContentType>();
|
||||
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
@@ -868,9 +935,19 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
// 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);
|
||||
|
||||
IContentType contentType;
|
||||
if (contentTypes.ContainsKey(dto.ContentVersionDto.ContentDto.ContentTypeId))
|
||||
{
|
||||
contentType = contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId];
|
||||
}
|
||||
else
|
||||
{
|
||||
contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
|
||||
contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId] = contentType;
|
||||
}
|
||||
|
||||
content[i] = ContentFactory.BuildEntity(dto, contentType);
|
||||
|
||||
// need template
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
@@ -891,7 +968,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
var propertyData = GetPropertyCollection(sqlIds, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
@@ -911,7 +988,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity) cc).ResetDirtyProperties(false);
|
||||
cc.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return content;
|
||||
@@ -928,8 +1005,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
{
|
||||
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
|
||||
|
||||
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
var content = factory.BuildEntity(dto);
|
||||
var content = ContentFactory.BuildEntity(dto, contentType);
|
||||
|
||||
//Check if template id is set on DocumentDto, and get ITemplate if it is.
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IMedia>(sqlClause, query);
|
||||
var sql = translator.Translate()
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
|
||||
return ProcessQuery(sql);
|
||||
}
|
||||
@@ -84,18 +84,23 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
|
||||
|
||||
protected override Sql GetBaseQuery(BaseQueryType queryType)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsContentVersion.contentId" : "*"))
|
||||
.From<ContentVersionDto>(SqlSyntax)
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId, SqlSyntax)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select(isCount ? "COUNT(*)" : "*")
|
||||
.From<ContentVersionDto>()
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
return sql;
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
@@ -148,6 +153,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var content = new IMedia[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
|
||||
//track the looked up content types, even though the content types are cached
|
||||
// they still need to be deep cloned out of the cache and we don't want to add
|
||||
// the overhead of deep cloning them on every item in this loop
|
||||
var contentTypes = new Dictionary<int, IMediaType>();
|
||||
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
@@ -165,9 +175,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
// 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);
|
||||
|
||||
IMediaType contentType;
|
||||
if (contentTypes.ContainsKey(dto.ContentDto.ContentTypeId))
|
||||
{
|
||||
contentType = contentTypes[dto.ContentDto.ContentTypeId];
|
||||
}
|
||||
else
|
||||
{
|
||||
contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
|
||||
contentTypes[dto.ContentDto.ContentTypeId] = contentType;
|
||||
}
|
||||
|
||||
content[i] = MediaFactory.BuildEntity(dto, contentType);
|
||||
|
||||
// need properties
|
||||
defs.Add(new DocumentDefinition(
|
||||
@@ -195,7 +215,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity) cc).ResetDirtyProperties(false);
|
||||
cc.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return content;
|
||||
@@ -205,26 +225,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
var mediaType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
|
||||
var content = CreateMediaFromDto(dto, versionId, sql);
|
||||
|
||||
var factory = new MediaFactory(mediaType, NodeObjectTypeId, dto.NodeId);
|
||||
var media = factory.BuildEntity(dto);
|
||||
|
||||
var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.VersionId, media.UpdateDate, media.CreateDate, mediaType) });
|
||||
|
||||
media.Properties = properties[dto.NodeId];
|
||||
|
||||
//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;
|
||||
return content;
|
||||
}
|
||||
|
||||
public void RebuildXmlStructures(Func<IMedia, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
|
||||
@@ -245,7 +255,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
query = query
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
|
||||
query = query
|
||||
.Where<NodeDto>(x => x.NodeId > baseId)
|
||||
.Where<NodeDto>(x => x.NodeId > baseId, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
@@ -495,9 +505,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
|
||||
}
|
||||
|
||||
return GetPagedResultsByQuery<ContentVersionDto, Models.Media>(query, pageIndex, pageSize, out totalRecords,
|
||||
return GetPagedResultsByQuery<ContentVersionDto>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsContentVersion", "contentId"),
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
(sqlFull, sqlIds) => ProcessQuery(sqlFull), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
@@ -512,9 +522,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private IMedia CreateMediaFromDto(ContentVersionDto dto, Guid versionId, Sql docSql)
|
||||
{
|
||||
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
|
||||
|
||||
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
var media = factory.BuildEntity(dto);
|
||||
|
||||
var media = MediaFactory.BuildEntity(dto, contentType);
|
||||
|
||||
var docDef = new DocumentDefinition(dto.NodeId, versionId, media.UpdateDate, media.CreateDate, contentType);
|
||||
|
||||
|
||||
@@ -107,24 +107,29 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<int,IMembershipUser>
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
protected override Sql GetBaseQuery(BaseQueryType queryType)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select(isCount ? "COUNT(*)" : "*")
|
||||
.From<MemberDto>()
|
||||
.InnerJoin<ContentVersionDto>()
|
||||
.On<ContentVersionDto, MemberDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsMember.nodeId" : "*"))
|
||||
.From<MemberDto>(SqlSyntax)
|
||||
.InnerJoin<ContentVersionDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, MemberDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
//We're joining the type so we can do a query against the member type - not sure if this adds much overhead or not?
|
||||
// the execution plan says it doesn't so we'll go with that and in that case, it might be worth joining the content
|
||||
// types by default on the document and media repo's so we can query by content type there too.
|
||||
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
.InnerJoin<ContentTypeDto>(SqlSyntax)
|
||||
.On<ContentTypeDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.ContentTypeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
@@ -617,9 +622,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
|
||||
}
|
||||
|
||||
return GetPagedResultsByQuery<MemberDto, Member>(query, pageIndex, pageSize, out totalRecords,
|
||||
return GetPagedResultsByQuery<MemberDto>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsMember", "nodeId"),
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
(sqlFull, sqlIds) => ProcessQuery(sqlFull), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -27,8 +28,6 @@ using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
using SqlSyntax;
|
||||
|
||||
internal abstract class VersionableRepositoryBase<TId, TEntity> : PetaPocoRepositoryBase<TId, TEntity>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
@@ -384,12 +383,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
ON CustomPropData.CustomPropValContentId = umbracoNode.id
|
||||
", sortedInt, sortedDecimal, sortedDate, sortedString, nodeIdSelect.Item2, nodeIdSelect.Item1, versionQuery, sortedSql.Arguments.Length, newestQuery);
|
||||
|
||||
//insert this just above the first LEFT OUTER JOIN (for cmsDocument) or the last WHERE (everything else)
|
||||
string newSql;
|
||||
if (nodeIdSelect.Item1 == "cmsDocument")
|
||||
newSql = sortedSql.SQL.Insert(sortedSql.SQL.IndexOf("LEFT OUTER JOIN"), outerJoinTempTable);
|
||||
else
|
||||
newSql = sortedSql.SQL.Insert(sortedSql.SQL.LastIndexOf("WHERE"), outerJoinTempTable);
|
||||
//insert this just above the last WHERE
|
||||
string newSql = sortedSql.SQL.Insert(sortedSql.SQL.LastIndexOf("WHERE"), outerJoinTempTable);
|
||||
|
||||
var newArgs = sortedSql.Arguments.ToList();
|
||||
newArgs.Add(orderBy);
|
||||
@@ -406,11 +401,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
//no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique.
|
||||
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
|
||||
// is empty for many nodes)
|
||||
// see: http://issues.umbraco.org/issue/U4-8831
|
||||
sortedSql.OrderBy("umbracoNode.id");
|
||||
if (orderBySystemField && orderBy != "umbracoNode.id")
|
||||
{
|
||||
//no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique.
|
||||
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
|
||||
// is empty for many nodes)
|
||||
// see: http://issues.umbraco.org/issue/U4-8831
|
||||
sortedSql.OrderBy("umbracoNode.id");
|
||||
}
|
||||
|
||||
return sortedSql;
|
||||
|
||||
@@ -420,7 +418,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// A helper method for inheritors to get the paged results by query in a way that minimizes queries
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto">The type of the d.</typeparam>
|
||||
/// <typeparam name="TContentBase">The 'true' entity type (i.e. Content, Member, etc...)</typeparam>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <param name="pageIndex">Index of the page.</param>
|
||||
/// <param name="pageSize">Size of the page.</param>
|
||||
@@ -433,34 +430,30 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="System.ArgumentNullException">orderBy</exception>
|
||||
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto, TContentBase>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
|
||||
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
|
||||
Tuple<string, string> nodeIdSelect,
|
||||
Func<Sql, IEnumerable<TEntity>> processQuery,
|
||||
Func<Sql, Sql, IEnumerable<TEntity>> processQuery,
|
||||
string orderBy,
|
||||
Direction orderDirection,
|
||||
bool orderBySystemField,
|
||||
Func<Tuple<string, object[]>> defaultFilter = null)
|
||||
where TContentBase : class, IAggregateRoot, TEntity
|
||||
{
|
||||
if (orderBy == null) throw new ArgumentNullException("orderBy");
|
||||
|
||||
// Get base query
|
||||
var sqlBase = GetBaseQuery(false);
|
||||
// Get base query for returning IDs
|
||||
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
// Get base query for returning all data
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
|
||||
|
||||
if (query == null) query = new Query<TEntity>();
|
||||
var translator = new SqlTranslator<TEntity>(sqlBase, query);
|
||||
var sqlQuery = translator.Translate();
|
||||
|
||||
// Note we can't do multi-page for several DTOs like we can multi-fetch and are doing in PerformGetByQuery,
|
||||
// but actually given we are doing a Get on each one (again as in PerformGetByQuery), we only need the node Id.
|
||||
// So we'll modify the SQL.
|
||||
var sqlNodeIds = new Sql(
|
||||
sqlQuery.SQL.Replace("SELECT *", string.Format("SELECT {0}.{1}", nodeIdSelect.Item1, nodeIdSelect.Item2)),
|
||||
sqlQuery.Arguments);
|
||||
var translatorIds = new SqlTranslator<TEntity>(sqlBaseIds, query);
|
||||
var sqlQueryIds = translatorIds.Translate();
|
||||
var translatorFull = new SqlTranslator<TEntity>(sqlBaseFull, query);
|
||||
var sqlQueryFull = translatorFull.Translate();
|
||||
|
||||
//get sorted and filtered sql
|
||||
var sqlNodeIdsWithSort = GetSortedSqlForPagedResults(
|
||||
GetFilteredSqlForPagedResults(sqlNodeIds, defaultFilter),
|
||||
GetFilteredSqlForPagedResults(sqlQueryIds, defaultFilter),
|
||||
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
|
||||
|
||||
// Get page of results and total count
|
||||
@@ -476,31 +469,25 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Crete the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
|
||||
var args = sqlNodeIdsWithSort.Arguments;
|
||||
string sqlStringCount, sqlStringPage;
|
||||
Database.BuildPageQueries<TDto>(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage);
|
||||
Database.BuildPageQueries<TDto>(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage);
|
||||
|
||||
//if this is for sql server, the sqlPage will start with a SELECT * but we don't want that, we only want to return the nodeId
|
||||
sqlStringPage = sqlStringPage
|
||||
.Replace("SELECT *",
|
||||
//This ensures we only take the field name of the node id select and not the table name - since the resulting select
|
||||
// will ony work with the field name.
|
||||
"SELECT " + nodeIdSelect.Item2);
|
||||
|
||||
//We need to make this an inner join on the paged query
|
||||
var splitQuery = sqlQuery.SQL.Split(new[] { "WHERE " }, StringSplitOptions.None);
|
||||
var withInnerJoinSql = new Sql(splitQuery[0])
|
||||
//We need to make this FULL query an inner join on the paged ID query
|
||||
var splitQuery = sqlQueryFull.SQL.Split(new[] { "WHERE " }, StringSplitOptions.None);
|
||||
var fullQueryWithPagedInnerJoin = new Sql(splitQuery[0])
|
||||
.Append("INNER JOIN (")
|
||||
//join the paged query with the paged query arguments
|
||||
.Append(sqlStringPage, args)
|
||||
.Append(") temp ")
|
||||
.Append(string.Format("ON {0}.{1} = temp.{1}", nodeIdSelect.Item1, nodeIdSelect.Item2))
|
||||
//add the original where clause back with the original arguments
|
||||
.Where(splitQuery[1], sqlQuery.Arguments);
|
||||
.Where(splitQuery[1], sqlQueryIds.Arguments);
|
||||
|
||||
//get sorted and filtered sql
|
||||
var fullQuery = GetSortedSqlForPagedResults(
|
||||
GetFilteredSqlForPagedResults(withInnerJoinSql, defaultFilter),
|
||||
GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, defaultFilter),
|
||||
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
|
||||
return processQuery(fullQuery);
|
||||
|
||||
return processQuery(fullQuery, sqlNodeIdsWithSort);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -512,9 +499,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
|
||||
Sql docSql,
|
||||
IEnumerable<DocumentDefinition> documentDefs)
|
||||
IReadOnlyCollection<DocumentDefinition> documentDefs)
|
||||
{
|
||||
if (documentDefs.Any() == false) return new Dictionary<int, PropertyCollection>();
|
||||
if (documentDefs.Count == 0) return new Dictionary<int, PropertyCollection>();
|
||||
|
||||
//we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use
|
||||
// the statement to go get the property data for all of the items by using an inner join
|
||||
@@ -525,23 +512,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
var propSql = new Sql(@"SELECT cmsPropertyData.*
|
||||
FROM cmsPropertyData
|
||||
INNER JOIN cmsPropertyType
|
||||
ON cmsPropertyData.propertytypeid = cmsPropertyType.id
|
||||
INNER JOIN
|
||||
(" + string.Format(parsedOriginalSql, "cmsContent.nodeId, cmsContentVersion.VersionId") + @") as docData
|
||||
ON cmsPropertyData.versionId = docData.VersionId AND cmsPropertyData.contentNodeId = docData.nodeId
|
||||
LEFT OUTER JOIN cmsDataTypePreValues
|
||||
ON cmsPropertyType.dataTypeId = cmsDataTypePreValues.datatypeNodeId", docSql.Arguments);
|
||||
|
||||
var allPropertyData = Database.Fetch<PropertyDataDto>(propSql);
|
||||
|
||||
//This is a lazy access call to get all prevalue data for the data types that make up all of these properties which we use
|
||||
// below if any property requires tag support
|
||||
var allPreValues = new Lazy<IEnumerable<DataTypePreValueDto>>(() =>
|
||||
{
|
||||
var preValsSql = new Sql(@"SELECT a.id, a.value, a.sortorder, a.alias, a.datatypeNodeId
|
||||
//This retrieves all pre-values for all data types that are referenced for all property types
|
||||
// that exist in the data set.
|
||||
//Benchmarks show that eagerly loading these so that we can lazily read the property data
|
||||
// below (with the use of Query intead of Fetch) go about 30% faster, so we'll eagerly load
|
||||
// this now since we cannot execute another reader inside of reading the property data.
|
||||
var preValsSql = new Sql(@"SELECT a.id, a.value, a.sortorder, a.alias, a.datatypeNodeId
|
||||
FROM cmsDataTypePreValues a
|
||||
WHERE EXISTS(
|
||||
SELECT DISTINCT b.id as preValIdInner
|
||||
@@ -553,25 +529,85 @@ WHERE EXISTS(
|
||||
ON cmsPropertyType.contentTypeId = docData.contentType
|
||||
WHERE a.id = b.id)", docSql.Arguments);
|
||||
|
||||
return Database.Fetch<DataTypePreValueDto>(preValsSql);
|
||||
});
|
||||
var allPreValues = Database.Fetch<DataTypePreValueDto>(preValsSql);
|
||||
|
||||
//It's Important with the sort order here! We require this to be sorted by node id,
|
||||
// this is required because this data set can be huge depending on the page size. Due
|
||||
// to it's size we need to be smart about iterating over the property values to build
|
||||
// the document. Before we used to use Linq to get the property data for a given content node
|
||||
// and perform a Distinct() call. This kills performance because that would mean if we had 7000 nodes
|
||||
// and on each iteration we will perform a lookup on potentially 100,000 property rows against the node
|
||||
// id which turns out to be a crazy amount of iterations. Instead we know it's sorted by this value we'll
|
||||
// keep an index stored of the rows being read so we never have to re-iterate the entire data set
|
||||
// on each document iteration.
|
||||
var propSql = new Sql(@"SELECT cmsPropertyData.*
|
||||
FROM cmsPropertyData
|
||||
INNER JOIN cmsPropertyType
|
||||
ON cmsPropertyData.propertytypeid = cmsPropertyType.id
|
||||
INNER JOIN
|
||||
(" + string.Format(parsedOriginalSql, "cmsContent.nodeId, cmsContentVersion.VersionId") + @") as docData
|
||||
ON cmsPropertyData.versionId = docData.VersionId AND cmsPropertyData.contentNodeId = docData.nodeId
|
||||
ORDER BY contentNodeId, propertytypeid
|
||||
", docSql.Arguments);
|
||||
|
||||
//This does NOT fetch all data into memory in a list, this will read
|
||||
// over the records as a data reader, this is much better for performance and memory,
|
||||
// but it means that during the reading of this data set, nothing else can be read
|
||||
// from SQL server otherwise we'll get an exception.
|
||||
var allPropertyData = Database.Query<PropertyDataDto>(propSql);
|
||||
|
||||
var result = new Dictionary<int, PropertyCollection>();
|
||||
|
||||
var propertiesWithTagSupport = new Dictionary<string, SupportTagsAttribute>();
|
||||
//used to track the resolved composition property types per content type so we don't have to re-resolve (ToArray) the list every time
|
||||
var resolvedCompositionProperties = new Dictionary<int, PropertyType[]>();
|
||||
|
||||
//iterate each definition grouped by it's content type - this will mean less property type iterations while building
|
||||
// up the property collections
|
||||
foreach (var compositionGroup in documentDefs.GroupBy(x => x.Composition))
|
||||
//keep track of the current property data item being enumerated
|
||||
var propertyDataSetEnumerator = allPropertyData.GetEnumerator();
|
||||
|
||||
try
|
||||
{
|
||||
var compositionProperties = compositionGroup.Key.CompositionPropertyTypes.ToArray();
|
||||
|
||||
foreach (var def in compositionGroup)
|
||||
//This must be sorted by node id because this is how we are sorting the query to lookup property types above,
|
||||
// which allows us to more efficiently iterate over the large data set of property values
|
||||
foreach (var def in documentDefs.OrderBy(x => x.Id))
|
||||
{
|
||||
var propertyDataDtos = allPropertyData.Where(x => x.NodeId == def.Id).Distinct();
|
||||
//get the resolved proeprties from our local cache, or resolve them and put them in cache
|
||||
PropertyType[] compositionProperties;
|
||||
if (resolvedCompositionProperties.ContainsKey(def.Composition.Id))
|
||||
{
|
||||
compositionProperties = resolvedCompositionProperties[def.Composition.Id];
|
||||
}
|
||||
else
|
||||
{
|
||||
compositionProperties = def.Composition.CompositionPropertyTypes.ToArray();
|
||||
resolvedCompositionProperties[def.Composition.Id] = compositionProperties;
|
||||
}
|
||||
|
||||
var propertyFactory = new PropertyFactory(compositionProperties, def.Version, def.Id, def.CreateDate, def.VersionDate);
|
||||
var properties = propertyFactory.BuildEntity(propertyDataDtos.ToArray()).ToArray();
|
||||
var propertyDataDtos = new List<PropertyDataDto>();
|
||||
|
||||
//Check if there is a current enumerated item and check if we match and add it
|
||||
if (propertyDataSetEnumerator.Current != null)
|
||||
{
|
||||
if (propertyDataSetEnumerator.Current.NodeId == def.Id)
|
||||
{
|
||||
propertyDataDtos.Add(propertyDataSetEnumerator.Current);
|
||||
}
|
||||
}
|
||||
//Move to the next position, see if we match and add it, if not exit
|
||||
while (propertyDataSetEnumerator.MoveNext())
|
||||
{
|
||||
if (propertyDataSetEnumerator.Current.NodeId == def.Id)
|
||||
{
|
||||
propertyDataDtos.Add(propertyDataSetEnumerator.Current);
|
||||
}
|
||||
else
|
||||
{
|
||||
//the node id has changed so we need to exit the loop,
|
||||
//the enumerator position will be maintained
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var properties = PropertyFactory.BuildEntity(propertyDataDtos, compositionProperties, def.CreateDate, def.VersionDate).ToArray();
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
@@ -588,7 +624,7 @@ WHERE EXISTS(
|
||||
propertiesWithTagSupport[property.PropertyType.PropertyEditorAlias] = tagSupport;
|
||||
|
||||
//this property has tags, so we need to extract them and for that we need the prevals which we've already looked up
|
||||
var preValData = allPreValues.Value.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId)
|
||||
var preValData = allPreValues.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
@@ -609,8 +645,13 @@ WHERE EXISTS(
|
||||
result[def.Id] = new PropertyCollection(properties);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
propertyDataSetEnumerator.Dispose();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public class DocumentDefinition
|
||||
@@ -722,5 +763,12 @@ WHERE EXISTS(
|
||||
|
||||
return allsuccess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For Paging, repositories must support returning different query for the query type specified
|
||||
/// </summary>
|
||||
/// <param name="queryType"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract Sql GetBaseQuery(BaseQueryType queryType);
|
||||
}
|
||||
}
|
||||
@@ -585,7 +585,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
[Obsolete("Use the overload with 'long' parameter types instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
public IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
{
|
||||
long total;
|
||||
var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter);
|
||||
@@ -604,7 +604,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="orderDirection">Direction to order by</param>
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
public IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
{
|
||||
return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter);
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace Umbraco.Core.Services
|
||||
[Obsolete("Use the overload with 'long' parameter types instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords,
|
||||
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
|
||||
@@ -273,7 +273,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace Umbraco.Core.Services
|
||||
[Obsolete("Use the overload with 'long' parameter types instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords,
|
||||
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
|
||||
@@ -184,7 +184,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
|
||||
|
||||
@@ -451,7 +451,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
[Obsolete("Use the overload with 'long' parameter types instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
public IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
{
|
||||
long total;
|
||||
var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter);
|
||||
@@ -470,7 +470,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="orderDirection">Direction to order by</param>
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
public IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
{
|
||||
return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter);
|
||||
}
|
||||
|
||||
@@ -510,6 +510,7 @@
|
||||
<Compile Include="Persistence\RecordPersistenceType.cs" />
|
||||
<Compile Include="Persistence\Relators\AccessRulesRelator.cs" />
|
||||
<Compile Include="Persistence\Repositories\AuditRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\BaseQueryType.cs" />
|
||||
<Compile Include="Persistence\Repositories\EntityContainerRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\DomainRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\ExternalLoginRepository.cs" />
|
||||
|
||||
@@ -34,18 +34,24 @@
|
||||
fields: {},
|
||||
file: file
|
||||
}).progress(function (evt) {
|
||||
|
||||
// hack: in some browsers the progress event is called after success
|
||||
// this prevents the UI from going back to a uploading state
|
||||
if(vm.zipFile.uploadStatus !== "done" && vm.zipFile.uploadStatus !== "error") {
|
||||
|
||||
// set view state to uploading
|
||||
vm.state = 'uploading';
|
||||
// set view state to uploading
|
||||
vm.state = 'uploading';
|
||||
|
||||
// calculate progress in percentage
|
||||
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
|
||||
// calculate progress in percentage
|
||||
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
|
||||
|
||||
// set percentage property on file
|
||||
vm.zipFile.uploadProgress = progressPercentage;
|
||||
// set percentage property on file
|
||||
vm.zipFile.uploadProgress = progressPercentage;
|
||||
|
||||
// set uploading status on file
|
||||
vm.zipFile.uploadStatus = "uploading";
|
||||
// set uploading status on file
|
||||
vm.zipFile.uploadStatus = "uploading";
|
||||
|
||||
}
|
||||
|
||||
}).success(function (data, status, headers, config) {
|
||||
|
||||
|
||||
@@ -166,6 +166,8 @@ namespace Umbraco.Web.WebServices
|
||||
var msg = ValidateLuceneIndexer(indexerName, out indexer);
|
||||
if (msg.IsSuccessStatusCode)
|
||||
{
|
||||
LogHelper.Info<ExamineManagementApiController>(string.Format("Rebuilding index '{0}'", indexerName));
|
||||
|
||||
//remove it in case there's a handler there alraedy
|
||||
indexer.IndexOperationComplete -= Indexer_IndexOperationComplete;
|
||||
//now add a single handler
|
||||
@@ -201,6 +203,8 @@ namespace Umbraco.Web.WebServices
|
||||
//ensure it's not listening anymore
|
||||
indexer.IndexOperationComplete -= Indexer_IndexOperationComplete;
|
||||
|
||||
LogHelper.Info<ExamineManagementApiController>(string.Format("Rebuilding index '{0}' done, {1} items committed (can differ from the number of items in the index)", indexer.Name, indexer.CommitCount));
|
||||
|
||||
var cacheKey = "temp_indexing_op_" + indexer.Name;
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(cacheKey);
|
||||
}
|
||||
@@ -266,7 +270,10 @@ namespace Umbraco.Web.WebServices
|
||||
var val = p.GetValue(indexer, null);
|
||||
if (val == null)
|
||||
{
|
||||
LogHelper.Warn<ExamineManagementApiController>("Property value was null when setting up property on indexer: " + indexer.Name + " property: " + p.Name);
|
||||
// Do not warn for new new attribute that is optional
|
||||
if(string.Equals(p.Name, "DirectoryFactory", StringComparison.InvariantCultureIgnoreCase) == false)
|
||||
LogHelper.Warn<ExamineManagementApiController>("Property value was null when setting up property on indexer: " + indexer.Name + " property: " + p.Name);
|
||||
|
||||
val = string.Empty;
|
||||
}
|
||||
indexerModel.ProviderProperties.Add(p.Name, val.ToString());
|
||||
|
||||
@@ -497,7 +497,7 @@ namespace UmbracoExamine
|
||||
IContent[] descendants;
|
||||
if (SupportUnpublishedContent)
|
||||
{
|
||||
descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total).ToArray();
|
||||
descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "umbracoNode.id").ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user