diff --git a/src/Orchard.Web/Core/Common/Extensions/ContentItemExtensions.cs b/src/Orchard.Web/Core/Common/Extensions/ContentItemExtensions.cs new file mode 100644 index 000000000..c1cc13642 --- /dev/null +++ b/src/Orchard.Web/Core/Common/Extensions/ContentItemExtensions.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.Core.Common.Models; + +namespace Orchard.ContentManagement +{ + + public class EagerlyLoadQueryResult + { + public EagerlyLoadQueryResult(IEnumerable items, IContentManager contentManager) { + Result = items; + ContentManager = contentManager; + } + public IEnumerable Result { get; set; } + public IContentManager ContentManager { get; set; } + } + public static class ContentItemExtensions + { + const int MaxPageSize = 2000; + public static EagerlyLoadQueryResult LoadContainerContentItems(this IList items, IContentManager contentManager, int maximumLevel = 0) where T : class, IContent { + var eagerlyLoadQueryResult = new EagerlyLoadQueryResult(items, contentManager); + return eagerlyLoadQueryResult.IncludeContainerContentItems(maximumLevel); + } + + public static EagerlyLoadQueryResult IncludeContainerContentItems(this IContentQuery query, int maximumLevel = 0) where T : class, IContent{ + var manager = query.ContentManager; + var eagerlyLoadQueryResult = new EagerlyLoadQueryResult(query.List(), manager); + return eagerlyLoadQueryResult.IncludeContainerContentItems(maximumLevel); + } + + public static EagerlyLoadQueryResult IncludeContainerContentItems(this EagerlyLoadQueryResult eagerlyLoadQueryResult, int maximumLevel = 0) where T : class, IContent { + + var containerIds = new HashSet(); + var objectsToLoad = eagerlyLoadQueryResult.Result.ToList(); + foreach (var part in objectsToLoad) + { + var commonPart = part.As(); + if (commonPart != null && commonPart.Record.Container != null && !containerIds.Contains(commonPart.Record.Container.Id)) + containerIds.Add(commonPart.Record.Container.Id); + } + var containersDictionary = eagerlyLoadQueryResult.ContentManager.GetTooMany(containerIds, VersionOptions.Latest, QueryHints.Empty) + .ToDictionary(c => c.ContentItem.Id); + foreach (var resultPart in objectsToLoad) + { + IContent container = null; + var commonPart = resultPart.As(); + 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 GetTooMany(this IContentManager contentManager, IEnumerable ids, VersionOptions versionOptions, QueryHints queryHints) where T : class, IContent { + if (ids == null) + return null; + var result = new List(); + var nextIdList = new List(); + + 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(ids.Skip(2000 * page).Take(pageSize), versionOptions, queryHints)); + } + } + return result; + } + } +} diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj index 7929c2a2e..0bcbd08cd 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -98,6 +98,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Extensions/ContentItemExtensions.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Extensions/ContentItemExtensions.cs new file mode 100644 index 000000000..c46a4888e --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Extensions/ContentItemExtensions.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 LoadMediaLibraryPickerFields(this IList items, IContentManager contentManager, int maximumLevel = 0) where T : class, IContent { + var eagerlyLoadQueryResult = new EagerlyLoadQueryResult(items, contentManager); + return eagerlyLoadQueryResult.IncludeMediaLibraryPickerFields(); + } + + public static EagerlyLoadQueryResult IncludeMediaLibraryPickerFields(this IContentQuery query) where T : class, IContent { + var manager = query.ContentManager; + var eagerlyLoadQueryResult = new EagerlyLoadQueryResult(query.List(), manager); + return eagerlyLoadQueryResult.IncludeMediaLibraryPickerFields(); + } + + public static EagerlyLoadQueryResult IncludeMediaLibraryPickerFields(this EagerlyLoadQueryResult eagerlyLoadQueryResult) where T : class, IContent { + var containerIds = new HashSet(); + foreach (var part in eagerlyLoadQueryResult.Result) { + var mediaLibraryPickerFields = part.ContentItem.Parts.SelectMany(p => p.Fields.Where(f => f is MediaLibraryPickerField).Cast()); + var ids = mediaLibraryPickerFields.SelectMany(f => f.Ids); + foreach (var id in ids) { + if (!containerIds.Contains(id)) + containerIds.Add(id); + } + } + Dictionary containersDictionary = eagerlyLoadQueryResult.ContentManager.GetTooMany(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()); + foreach (var mediaLibraryPickerField in mediaLibraryPickerFields) { + var preloadedMedias = new List(); + foreach (var mediaId in mediaLibraryPickerField.Ids) { + MediaPart preloadedMedia = null; + if (containersDictionary.TryGetValue(mediaId, out preloadedMedia)) + preloadedMedias.Add(preloadedMedia); + } + mediaLibraryPickerField.MediaParts = preloadedMedias; + } + } + return eagerlyLoadQueryResult; + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs index fac620f9e..77294def4 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs @@ -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> _contentItems; + internal LazyField> _contentItems = new LazyField>(); public int[] Ids { get { return DecodeIds(Storage.Get()); } @@ -19,6 +20,7 @@ namespace Orchard.MediaLibrary.Fields { get { return _contentItems != null ? _contentItems.Value : Enumerable.Empty(); } + set { _contentItems.Value = value; } } /// diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Handlers/MediaLibraryPickerFieldHandler.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Handlers/MediaLibraryPickerFieldHandler.cs index 8f29e137b..a66948f52 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Handlers/MediaLibraryPickerFieldHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Handlers/MediaLibraryPickerFieldHandler.cs @@ -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) { - + IContentManager contentManager, + 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()); // 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>(() => _contentManager.GetMany(localField.Ids, VersionOptions.Published, QueryHints.Empty).ToList()); + localField._contentItems.Loader(() => _contentManager.GetMany(localField.Ids, VersionOptions.Published, QueryHints.Empty).ToList()); } } } diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj index 8df0d0982..969f9533e 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Orchard.MediaLibrary.csproj @@ -127,6 +127,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Extensions/ContentItemExtensions.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Extensions/ContentItemExtensions.cs new file mode 100644 index 000000000..ec8b7d16a --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Extensions/ContentItemExtensions.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 LoadTaxonomyFields(this IList items, IContentManager contentManager, bool loadTermsContainter) where T : class, IContent { + var eagerlyLoadQueryResult = new EagerlyLoadQueryResult(items, contentManager); + return eagerlyLoadQueryResult.IncludeTaxonomyFields(loadTermsContainter); + } + + public static EagerlyLoadQueryResult IncludeTaxonomyFields(this IContentQuery query, bool loadTermsContainter) where T : class, IContent { + var manager = query.ContentManager; + query = query.Join().WithQueryHints(new QueryHints().ExpandRecords("TermsPartRecord.Terms")); + + var eagerlyLoadQueryResult = new EagerlyLoadQueryResult(query.List(), manager); + + return eagerlyLoadQueryResult.IncludeTaxonomyFields(loadTermsContainter); + } + + public static EagerlyLoadQueryResult IncludeTaxonomyFields(this EagerlyLoadQueryResult eagerlyLoadQueryResult, bool loadTermsContainter) where T : class, IContent { + var contentManager = eagerlyLoadQueryResult.ContentManager as DefaultContentManager; + var session = contentManager.TransactionManager.GetSession(); + + Dictionary> termsTermRecordIdsDictionary = new Dictionary>(); + var termsIds = new HashSet(); + List queryResult = new List(); + 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()).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().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 TermsRecordFieldDictionary = new Dictionary(); + TermsRecordFieldDictionary.Add(termRecordId, (string)keyValue[2]); + termsTermRecordIdsDictionary.Add((int)keyValue[0], TermsRecordFieldDictionary); + } + } + + var termsDictionary = eagerlyLoadQueryResult.ContentManager.GetTooMany(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()); + var preloadedTerms = new List(); + foreach (var field in fields) { + var preloadedFieldParts = new List(); + TermPart preloadedPart = null; + Dictionary 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(termsDictionary.Values, eagerlyLoadQueryResult.ContentManager); + pendingResults.IncludeContainerContentItems(1); + } + } + + return eagerlyLoadQueryResult; + } + } +} + diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Fields/TaxonomyField.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Fields/TaxonomyField.cs index e51cfc443..f127ab32f 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Fields/TaxonomyField.cs +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Fields/TaxonomyField.cs @@ -19,6 +19,7 @@ namespace Orchard.Taxonomies.Fields { /// public IEnumerable Terms { get { return TermsField.Value; } + set { TermsField.Value = value; } } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Models/TermsPart.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Models/TermsPart.cs index 8400350f4..822b020b1 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Models/TermsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Models/TermsPart.cs @@ -11,6 +11,9 @@ namespace Orchard.Taxonomies.Models { public class TermsPart : ContentPart { public IList Terms { get { return Record.Terms; } } internal LazyField> _termParts; - public IEnumerable TermParts { get { return _termParts.Value; } } + public IEnumerable TermParts { + get { return _termParts.Value; } + set { _termParts.Value = value; } + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Orchard.Taxonomies.csproj b/src/Orchard.Web/Modules/Orchard.Taxonomies/Orchard.Taxonomies.csproj index 558129785..ed88ef7ae 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Orchard.Taxonomies.csproj +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Orchard.Taxonomies.csproj @@ -51,6 +51,10 @@ 5 + + ..\..\..\packages\Iesi.Collections.4.0.0.4000\lib\net40\Iesi.Collections.dll + True + ..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll @@ -60,6 +64,10 @@ ..\..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll True + + ..\..\..\packages\NHibernate.4.0.1.4000\lib\net40\NHibernate.dll + True + 3.5 @@ -118,6 +126,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/packages.config b/src/Orchard.Web/Modules/Orchard.Taxonomies/packages.config index 61ca9fb6f..e3f68e8c7 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/packages.config +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/packages.config @@ -1,5 +1,6 @@  + diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs index 20b570714..ac9878865 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs @@ -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; diff --git a/src/Orchard/ContentManagement/DefaultContentManager.cs b/src/Orchard/ContentManagement/DefaultContentManager.cs index 07975549e..1a1be91de 100644 --- a/src/Orchard/ContentManagement/DefaultContentManager.cs +++ b/src/Orchard/ContentManagement/DefaultContentManager.cs @@ -33,7 +33,7 @@ namespace Orchard.ContentManagement { private readonly ICacheManager _cacheManager; private readonly Func _contentManagerSession; private readonly Lazy _contentDisplay; - private readonly Lazy _transactionManager; + private readonly Lazy _transactionManager; private readonly Lazy> _handlers; private readonly Lazy> _identityResolverSelectors; private readonly Lazy> _sqlStatementProviders; @@ -78,7 +78,11 @@ namespace Orchard.ContentManagement { public ILogger Logger { get; set; } public IEnumerable Handlers { - get { return _handlers.Value; } + get { return _handlers.Value; } + } + + public ITransactionManager TransactionManager { + get { return _transactionManager.Value; } } public IEnumerable GetContentTypeDefinitions() {