mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-25 19:17:13 +08:00
Added eagerly loading extensions (#7861)
This commit is contained in:
committed by
Sébastien Ros
parent
102d386d03
commit
fd3ad79dca
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Orchard.Taxonomies.Fields {
|
||||
/// </summary>
|
||||
public IEnumerable<TermPart> Terms {
|
||||
get { return TermsField.Value; }
|
||||
set { TermsField.Value = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -78,7 +78,11 @@ namespace Orchard.ContentManagement {
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public IEnumerable<IContentHandler> Handlers {
|
||||
get { return _handlers.Value; }
|
||||
get { return _handlers.Value; }
|
||||
}
|
||||
|
||||
public ITransactionManager TransactionManager {
|
||||
get { return _transactionManager.Value; }
|
||||
}
|
||||
|
||||
public IEnumerable<ContentTypeDefinition> GetContentTypeDefinitions() {
|
||||
|
||||
Reference in New Issue
Block a user