Added eagerly loading extensions (#7861)

This commit is contained in:
Sergio Navarro
2017-11-09 21:10:03 +01:00
committed by Sébastien Ros
parent 102d386d03
commit fd3ad79dca
13 changed files with 286 additions and 14 deletions

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Core.Common.Models;
namespace Orchard.ContentManagement
{
public class EagerlyLoadQueryResult<T>
{
public EagerlyLoadQueryResult(IEnumerable<T> items, IContentManager contentManager) {
Result = items;
ContentManager = contentManager;
}
public IEnumerable<T> Result { get; set; }
public IContentManager ContentManager { get; set; }
}
public static class ContentItemExtensions
{
const int MaxPageSize = 2000;
public static EagerlyLoadQueryResult<T> LoadContainerContentItems<T>(this IList<T> items, IContentManager contentManager, int maximumLevel = 0) where T : class, IContent {
var eagerlyLoadQueryResult = new EagerlyLoadQueryResult<T>(items, contentManager);
return eagerlyLoadQueryResult.IncludeContainerContentItems(maximumLevel);
}
public static EagerlyLoadQueryResult<T> IncludeContainerContentItems<T>(this IContentQuery<T> query, int maximumLevel = 0) where T : class, IContent{
var manager = query.ContentManager;
var eagerlyLoadQueryResult = new EagerlyLoadQueryResult<T>(query.List(), manager);
return eagerlyLoadQueryResult.IncludeContainerContentItems(maximumLevel);
}
public static EagerlyLoadQueryResult<T> IncludeContainerContentItems<T>(this EagerlyLoadQueryResult<T> eagerlyLoadQueryResult, int maximumLevel = 0) where T : class, IContent {
var containerIds = new HashSet<int>();
var objectsToLoad = eagerlyLoadQueryResult.Result.ToList();
foreach (var part in objectsToLoad)
{
var commonPart = part.As<CommonPart>();
if (commonPart != null && commonPart.Record.Container != null && !containerIds.Contains(commonPart.Record.Container.Id))
containerIds.Add(commonPart.Record.Container.Id);
}
var containersDictionary = eagerlyLoadQueryResult.ContentManager.GetTooMany<IContent>(containerIds, VersionOptions.Latest, QueryHints.Empty)
.ToDictionary(c => c.ContentItem.Id);
foreach (var resultPart in objectsToLoad)
{
IContent container = null;
var commonPart = resultPart.As<CommonPart>();
if (commonPart == null)
continue;
if (commonPart.Record.Container == null)
commonPart.Container = null;
else if (containersDictionary.TryGetValue(commonPart.Record.Container.Id, out container))
commonPart.Container = container;
}
if (maximumLevel > 0 && containersDictionary.Any())
{
containersDictionary.Values.ToList().LoadContainerContentItems(eagerlyLoadQueryResult.ContentManager, maximumLevel - 1);
}
return eagerlyLoadQueryResult;
}
public static IEnumerable<T> GetTooMany<T>(this IContentManager contentManager, IEnumerable<int> ids, VersionOptions versionOptions, QueryHints queryHints) where T : class, IContent {
if (ids == null)
return null;
var result = new List<T>();
var nextIdList = new List<int>();
var pageSize = MaxPageSize;
var maxPageIndex = Math.Floor((double)ids.Count() / MaxPageSize);
for (var page = 0; page <= maxPageIndex; page++) {
if (maxPageIndex == page) {
pageSize = ids.Count() % MaxPageSize;
}
if (pageSize > 0) {
result.AddRange(contentManager.GetMany<T>(ids.Skip(2000 * page).Take(pageSize), versionOptions, queryHints));
}
}
return result;
}
}
}

View File

@@ -98,6 +98,7 @@
<Compile Include="Common\Controllers\ErrorController.cs" />
<Compile Include="Common\DateEditor\DateEditorSettings.cs" />
<Compile Include="Common\Extensions\CommonMetaDataExtensions.cs" />
<Compile Include="Common\Extensions\ContentItemExtensions.cs" />
<Compile Include="Common\OwnerEditor\OwnerEditorSettings.cs" />
<Compile Include="Common\OwnerEditor\OwnerEditorDriver.cs" />
<Compile Include="Common\DateEditor\DateEditorDriver.cs" />

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.MediaLibrary.Fields;
using Orchard.MediaLibrary.Models;
namespace Orchard.ContentManagement
{
public static class ContentItemExtensions
{
public static EagerlyLoadQueryResult<T> LoadMediaLibraryPickerFields<T>(this IList<T> items, IContentManager contentManager, int maximumLevel = 0) where T : class, IContent {
var eagerlyLoadQueryResult = new EagerlyLoadQueryResult<T>(items, contentManager);
return eagerlyLoadQueryResult.IncludeMediaLibraryPickerFields();
}
public static EagerlyLoadQueryResult<T> IncludeMediaLibraryPickerFields<T>(this IContentQuery<T> query) where T : class, IContent {
var manager = query.ContentManager;
var eagerlyLoadQueryResult = new EagerlyLoadQueryResult<T>(query.List(), manager);
return eagerlyLoadQueryResult.IncludeMediaLibraryPickerFields();
}
public static EagerlyLoadQueryResult<T> IncludeMediaLibraryPickerFields<T>(this EagerlyLoadQueryResult<T> eagerlyLoadQueryResult) where T : class, IContent {
var containerIds = new HashSet<int>();
foreach (var part in eagerlyLoadQueryResult.Result) {
var mediaLibraryPickerFields = part.ContentItem.Parts.SelectMany(p => p.Fields.Where(f => f is MediaLibraryPickerField).Cast<MediaLibraryPickerField>());
var ids = mediaLibraryPickerFields.SelectMany(f => f.Ids);
foreach (var id in ids) {
if (!containerIds.Contains(id))
containerIds.Add(id);
}
}
Dictionary<int, MediaPart> containersDictionary = eagerlyLoadQueryResult.ContentManager.GetTooMany<MediaPart>(containerIds, VersionOptions.Published, QueryHints.Empty).ToDictionary(c => c.ContentItem.Id);
foreach (var resultPart in eagerlyLoadQueryResult.Result) {
var mediaLibraryPickerFields = resultPart.ContentItem.Parts.SelectMany(p => p.Fields.Where(f => f is MediaLibraryPickerField).Cast<MediaLibraryPickerField>());
foreach (var mediaLibraryPickerField in mediaLibraryPickerFields) {
var preloadedMedias = new List<MediaPart>();
foreach (var mediaId in mediaLibraryPickerField.Ids) {
MediaPart preloadedMedia = null;
if (containersDictionary.TryGetValue(mediaId, out preloadedMedia))
preloadedMedias.Add(preloadedMedia);
}
mediaLibraryPickerField.MediaParts = preloadedMedias;
}
}
return eagerlyLoadQueryResult;
}
}
}

View File

@@ -4,11 +4,12 @@ using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.FieldStorage;
using Orchard.MediaLibrary.Models;
using Orchard.ContentManagement.Utilities;
namespace Orchard.MediaLibrary.Fields {
public class MediaLibraryPickerField : ContentField {
private static readonly char[] separator = {'{', '}', ','};
internal Lazy<IEnumerable<MediaPart>> _contentItems;
internal LazyField<IEnumerable<MediaPart>> _contentItems = new LazyField<IEnumerable<MediaPart>>();
public int[] Ids {
get { return DecodeIds(Storage.Get<string>()); }
@@ -19,6 +20,7 @@ namespace Orchard.MediaLibrary.Fields {
get {
return _contentItems != null ? _contentItems.Value : Enumerable.Empty<MediaPart>();
}
set { _contentItems.Value = value; }
}
/// <summary>

View File

@@ -7,37 +7,44 @@ using Orchard.ContentManagement.MetaData;
using Orchard.MediaLibrary.Fields;
using Orchard.MediaLibrary.Models;
namespace Orchard.MediaLibrary.Handlers {
public class MediaLibraryPickerFieldHandler : ContentHandler {
namespace Orchard.MediaLibrary.Handlers
{
public class MediaLibraryPickerFieldHandler : ContentHandler
{
private readonly IContentManager _contentManager;
private readonly IContentDefinitionManager _contentDefinitionManager;
public MediaLibraryPickerFieldHandler(
IContentManager contentManager,
IContentDefinitionManager contentDefinitionManager) {
IContentDefinitionManager contentDefinitionManager)
{
_contentManager = contentManager;
_contentDefinitionManager = contentDefinitionManager;
}
protected override void Loaded(LoadContentContext context) {
protected override void Loaded(LoadContentContext context)
{
base.Loaded(context);
InitilizeLoader(context.ContentItem);
}
private void InitilizeLoader(ContentItem contentItem) {
private void InitilizeLoader(ContentItem contentItem)
{
var fields = contentItem.Parts.SelectMany(x => x.Fields.OfType<MediaLibraryPickerField>());
// define lazy initializer for MediaLibraryPickerField.MediaParts
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(contentItem.ContentType);
if (contentTypeDefinition == null) {
if (contentTypeDefinition == null)
{
return;
}
foreach (var field in fields) {
foreach (var field in fields)
{
var localField = field;
localField._contentItems = new Lazy<IEnumerable<MediaPart>>(() => _contentManager.GetMany<MediaPart>(localField.Ids, VersionOptions.Published, QueryHints.Empty).ToList());
localField._contentItems.Loader(() => _contentManager.GetMany<MediaPart>(localField.Ids, VersionOptions.Published, QueryHints.Empty).ToList());
}
}
}

View File

@@ -127,6 +127,7 @@
<Content Include="Scripts\Web.config" />
<Content Include="Styles\Web.config" />
<Compile Include="Drivers\VectorImagePartDriver.cs" />
<Compile Include="Extensions\ContentItemExtensions.cs" />
<Compile Include="Extensions\MediaMetaDataExtensions.cs" />
<Compile Include="Factories\VectorImageFactory.cs" />
<Compile Include="Handlers\MediaItemHandler.cs" />

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.Taxonomies.Fields;
using Orchard.Taxonomies.Models;
namespace Orchard.ContentManagement
{
public static class ContentItemExtensions
{
public static EagerlyLoadQueryResult<T> LoadTaxonomyFields<T>(this IList<T> items, IContentManager contentManager, bool loadTermsContainter) where T : class, IContent {
var eagerlyLoadQueryResult = new EagerlyLoadQueryResult<T>(items, contentManager);
return eagerlyLoadQueryResult.IncludeTaxonomyFields(loadTermsContainter);
}
public static EagerlyLoadQueryResult<T> IncludeTaxonomyFields<T>(this IContentQuery<T> query, bool loadTermsContainter) where T : class, IContent {
var manager = query.ContentManager;
query = query.Join<TermsPartRecord>().WithQueryHints(new QueryHints().ExpandRecords("TermsPartRecord.Terms"));
var eagerlyLoadQueryResult = new EagerlyLoadQueryResult<T>(query.List(), manager);
return eagerlyLoadQueryResult.IncludeTaxonomyFields(loadTermsContainter);
}
public static EagerlyLoadQueryResult<T> IncludeTaxonomyFields<T>(this EagerlyLoadQueryResult<T> eagerlyLoadQueryResult, bool loadTermsContainter) where T : class, IContent {
var contentManager = eagerlyLoadQueryResult.ContentManager as DefaultContentManager;
var session = contentManager.TransactionManager.GetSession();
Dictionary<int, Dictionary<int, string>> termsTermRecordIdsDictionary = new Dictionary<int, Dictionary<int, string>>();
var termsIds = new HashSet<int>();
List<Object[]> queryResult = new List<Object[]>();
int pageSize = 2000;
var itemsCount = eagerlyLoadQueryResult.Result.Count();
var pagesCount = (itemsCount + pageSize - 1) / pageSize;
for (var page = 0; page < pagesCount; page++) {
var objectsToLoad = eagerlyLoadQueryResult.Result.Select(c => c.As<TermsPart>()).Where(t => t != null).ToList();
if (!objectsToLoad.Any()) {
continue;
}
StringBuilder sb = new StringBuilder();
sb.Append("SELECT tc.TermsPartRecord.Id,tc.TermRecord.id,tc.Field FROM Orchard.Taxonomies.Models.TermContentItem as tc ");
sb.Append("JOIN tc.TermRecord as tp WHERE ");
var count = 0;
foreach (var part in objectsToLoad) {
sb.Append("tc.TermsPartRecord.id = " + part.Id.ToString());
count++;
if (count < objectsToLoad.Count) {
sb.Append(" OR ");
}
}
var result = session.CreateQuery(sb.ToString());
queryResult = result.List<object[]>().ToList();
foreach (var keyValue in queryResult) {
var termRecordId = (int)keyValue[1];
if (!termsIds.Contains(termRecordId)) {
termsIds.Add(termRecordId);
}
if (termsTermRecordIdsDictionary.ContainsKey((int)keyValue[0])) {
termsTermRecordIdsDictionary[(int)keyValue[0]].Add(termRecordId, (string)keyValue[2]);
}
else {
Dictionary<int, string> TermsRecordFieldDictionary = new Dictionary<int, string>();
TermsRecordFieldDictionary.Add(termRecordId, (string)keyValue[2]);
termsTermRecordIdsDictionary.Add((int)keyValue[0], TermsRecordFieldDictionary);
}
}
var termsDictionary = eagerlyLoadQueryResult.ContentManager.GetTooMany<TermPart>(termsIds, VersionOptions.Published
, new QueryHints().ExpandRecords("ContentTypeRecord", "CommonPartRecord", "TermsPartRecord"))
.ToDictionary(c => c.ContentItem.Id);
foreach (var resultPart in objectsToLoad) {
var fields = resultPart.ContentItem.Parts.SelectMany(p => p.Fields.OfType<TaxonomyField>());
var preloadedTerms = new List<TermContentItemPart>();
foreach (var field in fields) {
var preloadedFieldParts = new List<TermPart>();
TermPart preloadedPart = null;
Dictionary<int, string> TermsRecordFieldDictionary;
if (termsTermRecordIdsDictionary.TryGetValue(resultPart.Id, out TermsRecordFieldDictionary)) {
foreach (var fieldWithTerm in TermsRecordFieldDictionary) {
if (fieldWithTerm.Value == field.Name && termsDictionary.TryGetValue(fieldWithTerm.Key, out preloadedPart)) {
preloadedFieldParts.Add(preloadedPart);
preloadedTerms.Add(new TermContentItemPart {
Field = field.Name,
TermPart = preloadedPart
});
}
}
field.Terms = preloadedFieldParts;
}
resultPart.TermParts = preloadedTerms;
}
}
if (loadTermsContainter && termsDictionary.Any()) {
var pendingResults = new EagerlyLoadQueryResult<IContent>(termsDictionary.Values, eagerlyLoadQueryResult.ContentManager);
pendingResults.IncludeContainerContentItems(1);
}
}
return eagerlyLoadQueryResult;
}
}
}

View File

@@ -19,6 +19,7 @@ namespace Orchard.Taxonomies.Fields {
/// </summary>
public IEnumerable<TermPart> Terms {
get { return TermsField.Value; }
set { TermsField.Value = value; }
}
}
}

View File

@@ -11,6 +11,9 @@ namespace Orchard.Taxonomies.Models {
public class TermsPart : ContentPart<TermsPartRecord> {
public IList<TermContentItem> Terms { get { return Record.Terms; } }
internal LazyField<IEnumerable<TermContentItemPart>> _termParts;
public IEnumerable<TermContentItemPart> TermParts { get { return _termParts.Value; } }
public IEnumerable<TermContentItemPart> TermParts {
get { return _termParts.Value; }
set { _termParts.Value = value; }
}
}
}

View File

@@ -51,6 +51,10 @@
<LangVersion>5</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="Iesi.Collections, Version=4.0.0.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Iesi.Collections.4.0.0.4000\lib\net40\Iesi.Collections.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
@@ -60,6 +64,10 @@
<HintPath>..\..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NHibernate, Version=4.0.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\NHibernate.4.0.1.4000\lib\net40\NHibernate.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
@@ -118,6 +126,7 @@
<Compile Include="Drivers\LocalizedTermPartDriver.cs" />
<Compile Include="EventHandlers\TermMovingEventHandler.cs" />
<Compile Include="Events\ITermLocalizationEventHandler.cs" />
<Compile Include="Extensions\ContentItemExtensions.cs" />
<Compile Include="Handlers\LocalizedTaxonomyFieldHandler.cs" />
<Compile Include="Handlers\LocalizedTaxonomyPartHandler.cs" />
<Compile Include="LocalizedTaxonomyMigration.cs" />

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Iesi.Collections" version="4.0.0.4000" targetFramework="net452" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />

View File

@@ -9,6 +9,7 @@ using Orchard.Mvc.Filters;
using Orchard.Themes;
using Orchard.UI.Admin;
using Orchard.Widgets.Services;
using Orchard.ContentManagement;
namespace Orchard.Widgets.Filters {
public class WidgetFilter : FilterProvider, IResultFilter {
@@ -50,6 +51,8 @@ namespace Orchard.Widgets.Filters {
}
var widgetParts = _widgetsService.GetWidgets(_layerEvaluationService.GetActiveLayerIds());
widgetParts.Select(w => w.ContentItem).ToList()
.LoadContainerContentItems(_orchardServices.ContentManager, 1);
// Build and add shape to zone.
var zones = workContext.Layout.Zones;

View File

@@ -81,6 +81,10 @@ namespace Orchard.ContentManagement {
get { return _handlers.Value; }
}
public ITransactionManager TransactionManager {
get { return _transactionManager.Value; }
}
public IEnumerable<ContentTypeDefinition> GetContentTypeDefinitions() {
return _contentDefinitionManager.ListTypeDefinitions();
}