Removes GetChildren and GetDescendants from IMediaService, paging is now required

This commit is contained in:
Shannon
2018-10-31 23:11:37 +11:00
parent 40eafe57b1
commit 25dcc3f2c1
9 changed files with 112 additions and 257 deletions

View File

@@ -37,79 +37,6 @@ namespace Umbraco.Core
return dirty.WasPropertyDirty("Published") && entity.Published;
}
/// <summary>
/// Returns a list of the current contents ancestors, not including the content itself.
/// </summary>
/// <param name="content">Current content</param>
/// <param name="contentService"></param>
/// <returns>An enumerable list of <see cref="IContent"/> objects</returns>
public static IEnumerable<IContent> Ancestors(this IContent content, IContentService contentService)
{
return contentService.GetAncestors(content);
}
/// <summary>
/// Returns the parent of the current content.
/// </summary>
/// <param name="content">Current content</param>
/// <param name="contentService"></param>
/// <returns>An <see cref="IContent"/> object</returns>
public static IContent Parent(this IContent content, IContentService contentService)
{
return contentService.GetById(content.ParentId);
}
#endregion
#region IMedia
/// <summary>
/// Returns a list of the current medias ancestors, not including the media itself.
/// </summary>
/// <param name="media">Current media</param>
/// <param name="mediaService"></param>
/// <returns>An enumerable list of <see cref="IMedia"/> objects</returns>
public static IEnumerable<IMedia> Ancestors(this IMedia media, IMediaService mediaService)
{
return mediaService.GetAncestors(media);
}
/// <summary>
/// Returns a list of the current medias children.
/// </summary>
/// <param name="media">Current media</param>
/// <param name="mediaService"></param>
/// <returns>An enumerable list of <see cref="IMedia"/> objects</returns>
public static IEnumerable<IMedia> Children(this IMedia media, IMediaService mediaService)
{
return mediaService.GetChildren(media.Id);
}
/// <summary>
/// Returns a list of the current medias descendants, not including the media itself.
/// </summary>
/// <param name="media">Current media</param>
/// <param name="mediaService"></param>
/// <returns>An enumerable list of <see cref="IMedia"/> objects</returns>
public static IEnumerable<IMedia> Descendants(this IMedia media, IMediaService mediaService)
{
return mediaService.GetDescendants(media);
}
/// <summary>
/// Returns the parent of the current media.
/// </summary>
/// <param name="media">Current media</param>
/// <param name="mediaService"></param>
/// <returns>An <see cref="IMedia"/> object</returns>
public static IMedia Parent(this IMedia media, IMediaService mediaService)
{
return mediaService.GetById(media.ParentId);
}
#endregion
/// <summary>

View File

@@ -109,9 +109,14 @@ namespace Umbraco.Core.Services
if (withDescendants)
{
var descendants = mediaService.GetDescendants(media).ToArray();
var currentChildren = descendants.Where(x => x.ParentId == media.Id);
SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, descendants, currentChildren, xml);
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
while (page * pageSize < total)
{
var children = mediaService.GetPagedChildren(media.Id, page++, pageSize, out total);
SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, children, xml);
}
}
return xml;
@@ -478,7 +483,7 @@ namespace Umbraco.Core.Services
}
// exports an IMedia item descendants.
private static void SerializeDescendants(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IMedia[] originalDescendants, IEnumerable<IMedia> children, XElement xml)
private static void SerializeChildren(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IEnumerable<IMedia> children, XElement xml)
{
foreach (var child in children)
{
@@ -486,12 +491,15 @@ namespace Umbraco.Core.Services
var childXml = Serialize(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, child);
xml.Add(childXml);
// capture id (out of closure) and get the grandChildren (children of the child)
var parentId = child.Id;
var grandChildren = originalDescendants.Where(x => x.ParentId == parentId);
// recurse
SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, originalDescendants, grandChildren, childXml);
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
while (page * pageSize < total)
{
var grandChildren = mediaService.GetPagedChildren(child.Id, page++, pageSize, out total);
// recurse
SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, grandChildren, childXml);
}
}
}
}

View File

@@ -78,13 +78,6 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IMedia"/></returns>
IMedia GetById(int id);
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Children from</param>
/// <returns>An Enumerable list of <see cref="IMedia"/> objects</returns>
IEnumerable<IMedia> GetChildren(int id);
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
/// </summary>
@@ -158,14 +151,7 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IMedia> filter);
/// <summary>
/// Gets descendants of a <see cref="IMedia"/> object by its Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve descendants from</param>
/// <returns>An Enumerable flat list of <see cref="IMedia"/> objects</returns>
IEnumerable<IMedia> GetDescendants(int id);
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by the Id of the <see cref="IContentType"/>
/// </summary>
@@ -321,13 +307,6 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IMedia"/> objects</returns>
IEnumerable<IMedia> GetAncestors(IMedia media);
/// <summary>
/// Gets descendants of a <see cref="IMedia"/> object by its Id
/// </summary>
/// <param name="media">The Parent <see cref="IMedia"/> object to retrieve descendants from</param>
/// <returns>An Enumerable flat list of <see cref="IMedia"/> objects</returns>
IEnumerable<IMedia> GetDescendants(IMedia media);
/// <summary>
/// Gets the parent of the current media as an <see cref="IMedia"/> item.
/// </summary>

View File

@@ -586,9 +586,6 @@ namespace Umbraco.Core.Services.Implement
/// <inheritdoc />
public IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IContent> filter)
{
if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex));
if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize));
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.ContentTree);
@@ -602,27 +599,22 @@ namespace Umbraco.Core.Services.Implement
totalChildren = 0;
return Enumerable.Empty<IContent>();
}
return GetPagedDescendants(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
}
return GetPagedDescendants(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
}
}
private IEnumerable<IContent> GetPagedDescendants(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IContent> filter)
private IEnumerable<IContent> GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IContent> filter)
{
if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex));
if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize));
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.ContentTree);
var query = Query<IContent>();
if (!contentPath.IsNullOrWhiteSpace())
query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar));
var query = Query<IContent>();
if (!contentPath.IsNullOrWhiteSpace())
query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar));
return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField));
}
return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField));
}
/// <summary>
@@ -1403,50 +1395,8 @@ namespace Umbraco.Core.Services.Implement
private void DeleteLocked(IScope scope, IContent content)
{
//TODO: Test this
// then recursively delete descendants, bottom-up
// just repository.Delete + an event
var stack = new Stack<IContent>();
stack.Push(content);
var level = 1;
while (stack.Count > 0)
void DoDelete(IContent c)
{
var c = stack.Peek();
if (c.Level == level)
{
var hasChildren = true;
while (hasChildren)
{
IContent last = null;
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
var countChildren = 0;
//get all children of c in pages
while (page * pageSize < total)
{
var children = GetPagedChildren(c.Id, page++, pageSize, out total);
foreach (var ci in children)
{
countChildren++;
stack.Push(ci);
last = ci;
}
if (countChildren == 0)
hasChildren = false; //exit, there are no children left to load
else
c = last; //recurse with the last child
}
}
}
c = stack.Pop();
level = c.Level;
_documentRepository.Delete(c);
var args = new DeleteEventArgs<IContent>(c, false); // raise event & get flagged files
scope.Events.Dispatch(Deleted, this, args, nameof(Deleted));
@@ -1455,6 +1405,18 @@ namespace Umbraco.Core.Services.Implement
_mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files
(file, e) => Logger.Error<ContentService>(e, "An error occurred while deleting file attached to nodes: {File}", file));
}
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
while (page * pageSize < total)
{
//get descendants - ordered from deepest to shallowest
var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, "Path", Direction.Descending);
foreach (var c in descendants)
DoDelete(c);
}
DoDelete(content);
}
//TODO:
@@ -1683,16 +1645,13 @@ namespace Umbraco.Core.Services.Implement
var total = long.MaxValue;
while(page * pageSize < total)
{
var descendants = GetPagedDescendants(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null);
var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null);
foreach (var descendant in descendants)
{
moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path
// update path and level since we do not update parentId
if (paths.ContainsKey(descendant.ParentId) == false)
Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path);
descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id;
Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]);
descendant.Level += levelDelta;
PerformMoveContentLocked(descendant, userId, trash);
}

View File

@@ -460,21 +460,6 @@ namespace Umbraco.Core.Services.Implement
}
}
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Children from</param>
/// <returns>An Enumerable list of <see cref="IMedia"/> objects</returns>
public IEnumerable<IMedia> GetChildren(int id)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.MediaTree);
var query = Query<IMedia>().Where(x => x.ParentId == id);
return _mediaRepository.Get(query).OrderBy(x => x.SortOrder);
}
}
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
/// </summary>
@@ -594,15 +579,10 @@ namespace Umbraco.Core.Services.Implement
/// <returns>An Enumerable list of <see cref="IMedia"/> objects</returns>
public IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IMedia> filter)
{
if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex));
if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize));
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.MediaTree);
var query = Query<IMedia>();
//if the id is System Root, then just get all
if (id != Constants.System.Root)
{
@@ -612,47 +592,22 @@ namespace Umbraco.Core.Services.Implement
totalChildren = 0;
return Enumerable.Empty<IMedia>();
}
query.Where(x => x.Path.SqlStartsWith(mediaPath[0].Path + ",", TextColumnType.NVarchar));
return GetPagedDescendantsLocked(mediaPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
}
return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField));
return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
}
}
/// <summary>
/// Gets descendants of a <see cref="IMedia"/> object by its Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve descendants from</param>
/// <returns>An Enumerable flat list of <see cref="IMedia"/> objects</returns>
public IEnumerable<IMedia> GetDescendants(int id)
private IEnumerable<IMedia> GetPagedDescendantsLocked(string mediaPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IMedia> filter)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.MediaTree);
var media = GetById(id);
if (media == null)
return Enumerable.Empty<IMedia>();
if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex));
if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize));
var pathMatch = media.Path + ",";
var query = Query<IMedia>().Where(x => x.Id != media.Id && x.Path.StartsWith(pathMatch));
return _mediaRepository.Get(query);
}
}
var query = Query<IMedia>();
if (!mediaPath.IsNullOrWhiteSpace())
query.Where(x => x.Path.SqlStartsWith(mediaPath + ",", TextColumnType.NVarchar));
/// <summary>
/// Gets descendants of a <see cref="IMedia"/> object by its Id
/// </summary>
/// <param name="media">The Parent <see cref="IMedia"/> object to retrieve descendants from</param>
/// <returns>An Enumerable flat list of <see cref="IMedia"/> objects</returns>
public IEnumerable<IMedia> GetDescendants(IMedia media)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.MediaTree);
var pathMatch = media.Path + ",";
var query = Query<IMedia>().Where(x => x.Id != media.Id && x.Path.StartsWith(pathMatch));
return _mediaRepository.Get(query);
}
return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField));
}
/// <summary>
@@ -865,25 +820,8 @@ namespace Umbraco.Core.Services.Implement
private void DeleteLocked(IScope scope, IMedia media)
{
// then recursively delete descendants, bottom-up
// just repository.Delete + an event
var stack = new Stack<IMedia>();
stack.Push(media);
var level = 1;
while (stack.Count > 0)
void DoDelete(IMedia c)
{
var c = stack.Peek();
IMedia[] cc;
if (c.Level == level)
while ((cc = c.Children(this).ToArray()).Length > 0)
{
foreach (var ci in cc)
stack.Push(ci);
c = cc[cc.Length - 1];
}
c = stack.Pop();
level = c.Level;
_mediaRepository.Delete(c);
var args = new DeleteEventArgs<IMedia>(c, false); // raise event & get flagged files
scope.Events.Dispatch(Deleted, this, args);
@@ -891,6 +829,18 @@ namespace Umbraco.Core.Services.Implement
_mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files
(file, e) => Logger.Error<MediaService>(e, "An error occurred while deleting file attached to nodes: {File}", file));
}
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
while(page * pageSize < total)
{
//get descendants - ordered from deepest to shallowest
var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, "Path", Direction.Descending);
foreach (var c in descendants)
DoDelete(c);
}
DoDelete(media);
}
//TODO:
@@ -1100,8 +1050,8 @@ namespace Umbraco.Core.Services.Implement
moves.Add(Tuple.Create(media, media.Path)); // capture original path
// get before moving, in case uow is immediate
var descendants = GetDescendants(media);
//need to store the original path to lookup descendants based on it below
var originalPath = media.Path;
// these will be updated by the repo because we changed parentId
//media.Path = (parent == null ? "-1" : parent.Path) + "," + media.Id;
@@ -1114,14 +1064,21 @@ namespace Umbraco.Core.Services.Implement
//paths[media.Id] = media.Path;
paths[media.Id] = (parent == null ? (parentId == Constants.System.RecycleBinMedia ? "-1,-21" : "-1") : parent.Path) + "," + media.Id;
foreach (var descendant in descendants)
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
while (page * pageSize < total)
{
moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path
var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null);
foreach (var descendant in descendants)
{
moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path
// update path and level since we do not update parentId
descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id;
descendant.Level += levelDelta;
PerformMoveMediaLocked(descendant, userId, trash);
// update path and level since we do not update parentId
descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id;
descendant.Level += levelDelta;
PerformMoveMediaLocked(descendant, userId, trash);
}
}
}

View File

@@ -1318,7 +1318,7 @@ namespace Umbraco.Web.Editors
xnames.Add(xcontent.Name);
if (xcontent.ParentId < -1)
xnames.Add("Recycle Bin");
xcontent = xcontent.Parent(Services.ContentService);
xcontent = Services.ContentService.GetParent(xcontent);
}
xnames.Reverse();
domainModel.Other = "/" + string.Join("/", xnames);

View File

@@ -271,7 +271,7 @@ namespace Umbraco.Web.Editors
// else proceed as usual
long totalChildren;
IMedia[] children;
List<IMedia> children;
if (pageNumber > 0 && pageSize > 0)
{
IQuery<IMedia> queryFilter = null;
@@ -287,12 +287,13 @@ namespace Umbraco.Web.Editors
id, (pageNumber - 1), pageSize,
out totalChildren,
orderBy, orderDirection, orderBySystemField,
queryFilter).ToArray();
queryFilter).ToList();
}
else
{
children = Services.MediaService.GetChildren(id).ToArray();
totalChildren = children.Length;
//better to not use this without paging where possible, currently only the sort dialog does
children = Services.MediaService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList();
totalChildren = children.Count;
}
if (totalChildren == 0)
@@ -663,7 +664,8 @@ namespace Umbraco.Web.Editors
" returned null");
//look for matching folder
folderMediaItem = mediaRoot.Children(Services.MediaService).FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Constants.Conventions.MediaTypes.Folder);
folderMediaItem = FindInChildren(mediaRoot.Id, folderName, Constants.Conventions.MediaTypes.Folder);
if (folderMediaItem == null)
{
//if null, create a folder
@@ -753,6 +755,23 @@ namespace Umbraco.Web.Editors
return Request.CreateResponse(HttpStatusCode.OK, tempFiles);
}
private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias)
{
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
while (page * pageSize < total)
{
var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total);
foreach (var c in children)
{
if (c.Name == nameToFind && c.ContentType.Alias == contentTypeAlias)
return c;
}
}
return null;
}
/// <summary>
/// Given a parent id which could be a GUID, UDI or an INT, this will resolve the INT
/// </summary>

View File

@@ -129,7 +129,7 @@ namespace Umbraco.Web.Routing
var parent = content;
do
{
parent = parent.ParentId > 0 ? parent.Parent(contentService) : null;
parent = parent.ParentId > 0 ? contentService.GetParent(parent) : null;
}
while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture)));

View File

@@ -304,10 +304,16 @@ namespace Umbraco.Web.Search
// branch
if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch))
{
var descendants = mediaService.GetDescendants(media);
foreach (var descendant in descendants)
const int pageSize = 500;
var page = 0;
var total = long.MaxValue;
while (page * pageSize < total)
{
ReIndexForMedia(descendant, descendant.Trashed == false);
var descendants = mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total);
foreach (var descendant in descendants)
{
ReIndexForMedia(descendant, descendant.Trashed == false);
}
}
}
}