diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Drivers/LocalizationPartDriver.cs b/src/Orchard.Web/Modules/Orchard.Localization/Drivers/LocalizationPartDriver.cs index 15de82106..ce99ab41e 100644 --- a/src/Orchard.Web/Modules/Orchard.Localization/Drivers/LocalizationPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Localization/Drivers/LocalizationPartDriver.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; @@ -18,6 +19,9 @@ namespace Orchard.Localization.Drivers { _cultureManager = cultureManager; _localizationService = localizationService; _contentManager = contentManager; + + publishedLocalizations = new Dictionary>(); + latestLocalizations = new Dictionary>(); } protected override DriverResult Display(LocalizationPart part, string displayType, dynamic shapeHelper) { @@ -69,10 +73,14 @@ namespace Orchard.Localization.Drivers { protected override DriverResult Editor(LocalizationPart part, IUpdateModel updater, dynamic shapeHelper) { var model = new EditLocalizationViewModel(); - - // GetCulture(part) is checked against null value, because the content culture has to be set only if it's not set already. - // model.SelectedCulture is checked against null value, because the editor group may not contain LocalizationPart when the content item is saved for the first time. - if (updater != null && updater.TryUpdateModel(model, TemplatePrefix, null, null) && GetCulture(part) == null && !string.IsNullOrEmpty(model.SelectedCulture)) { + if (updater != null && updater.TryUpdateModel(model, TemplatePrefix, null, null) + // GetCulture(part) is checked against null value, because the content + // culture has to be set only if it's not set already. + && GetCulture(part) == null + // model.SelectedCulture is checked against null value, because the editor + // group may not contain LocalizationPart when the content item is saved for + // the first time. + && !string.IsNullOrEmpty(model.SelectedCulture)) { _localizationService.SetContentCulture(part, model.SelectedCulture); } @@ -98,16 +106,52 @@ namespace Orchard.Localization.Drivers { return part.Culture != null ? part.Culture.Culture : null; } - private IEnumerable GetDisplayLocalizations(LocalizationPart part, VersionOptions versionOptions) { - return _localizationService.GetLocalizations(part.ContentItem, versionOptions) - .Where(c => c.Culture != null) - .ToList(); + private Dictionary> publishedLocalizations; + private Dictionary> latestLocalizations; + private IEnumerable GetDisplayLocalizations( + LocalizationPart part, VersionOptions versionOptions) { + + Func> actualMethod = () => + _localizationService.GetLocalizations(part.ContentItem, versionOptions) + .Where(c => c.Culture != null) + .ToList(); + // if the part has no assigned culture, and it does not belong to + // a translation group, it cannot possibly have localizations + if (GetCulture(part) == null && !part.HasTranslationGroup) { + actualMethod = () => Enumerable.Empty(); + // this empty list will be "cached" for the part for the duration + // a request, in order to prevent asking the db to return something + // that we know to not be there. This should prevent a class of + // deadlocks on the indexes for the LocalizationPartRecord table. + // The reasoning: + // part.HasTranslationGroup is false when the content is not a + // localization of any other content. It may be a MasterContent, + // but if that were the case it would have a value for its Culture. + // The condition here represents a new ContentItem, that has never + // had an assigned culture (whether it's actually new, or the + // LocalizationPart has just been welded to its type) + } + + if (versionOptions.IsPublished) { + if (!publishedLocalizations.ContainsKey(part.Id)) { + publishedLocalizations.Add( + part.Id, + actualMethod()); + } + return publishedLocalizations[part.Id]; + } else if (versionOptions.IsLatest) { + if (!latestLocalizations.ContainsKey(part.Id)) { + latestLocalizations.Add( + part.Id, + actualMethod()); + } + return latestLocalizations[part.Id]; + } + return actualMethod(); } private IEnumerable GetEditorLocalizations(LocalizationPart part) { - return _localizationService.GetLocalizations(part.ContentItem, VersionOptions.Latest) - .Where(c => c.Culture != null) - .ToList(); + return GetDisplayLocalizations(part, VersionOptions.Latest); } protected override void Importing(LocalizationPart part, ImportContentContext context) {