Refactoring: Add extension method for retrieval of language ISO codes if that's all we need (#20324)

* Retrieve only ISO codes from the database rather than full language objects if that's all we need.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Removed repository updates and migrated the new service method to an extension method.

* Fixed issue after merge.

* Removed left-over using

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kenn Jacobsen <kja@umbraco.dk>
This commit is contained in:
Andy Butland
2025-10-01 20:21:41 +02:00
committed by GitHub
parent 3f0428c8ef
commit 78f4caa2be
15 changed files with 64 additions and 35 deletions

View File

@@ -0,0 +1,16 @@
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Core.Extensions;
/// <summary>
/// Provides extension methods for <see cref="ILanguageService"/>.
/// </summary>
public static class LanguageServiceExtensions
{
/// <summary>
/// Retrieves all ISO codes of all languages.
/// </summary>
/// <param name="service">The <see cref="ILanguageService"/>.</param>
/// <returns>A collection of language ISO codes.</returns>
public static async Task<IEnumerable<string>> GetAllIsoCodesAsync(this ILanguageService service) => (await service.GetAllAsync()).Select(x => x.IsoCode);
}

View File

@@ -38,5 +38,11 @@ public interface ILanguageRepository : IReadWriteQueryRepository<int, ILanguage>
/// </remarks>
int? GetDefaultId();
/// <summary>
/// Gets multiple language ISO codes from the provided Ids.
/// </summary>
/// <param name="ids">The language Ids.</param>
/// <param name="throwOnNotFound">Indicates whether to throw an exception if the provided Id is not found as a language.</param>
/// <returns></returns>
string[] GetIsoCodesByIds(ICollection<int> ids, bool throwOnNotFound = true);
}

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Services;
@@ -42,7 +43,7 @@ public class PublishedUrlInfoProvider : IPublishedUrlInfoProvider
public async Task<ISet<UrlInfo>> GetAllAsync(IContent content)
{
HashSet<UrlInfo> urlInfos = [];
var cultures = (await _languageService.GetAllAsync()).Select(x => x.IsoCode).ToArray();
IEnumerable<string> cultures = await _languageService.GetAllIsoCodesAsync();
// First we get the urls of all cultures, using the published router, meaning we respect any extensions.
foreach (var culture in cultures)

View File

@@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PublishedCache;
@@ -118,7 +119,7 @@ public static class UrlProviderExtensions
// and, not only for those assigned to domains in the branch, because we want
// to show what GetUrl() would return, for every culture.
var urls = new HashSet<UrlInfo>();
var cultures = (await languageService.GetAllAsync()).Select(x => x.IsoCode).ToList();
IEnumerable<string> cultures = await languageService.GetAllIsoCodesAsync();
// get all URLs for all cultures
// in a HashSet, so de-duplicates too

View File

@@ -1,6 +1,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.Membership;
@@ -104,8 +105,8 @@ internal sealed class ContentEditingService
{
// If no cultures are provided, we are asking to validate all cultures. But if the user doesn't have access to all, we
// should only validate the ones they do.
var allCultures = (await _languageService.GetAllAsync()).Select(x => x.IsoCode).ToList();
return allowedCultures.Count == allCultures.Count ? null : allowedCultures;
IEnumerable<string> allCultures = await _languageService.GetAllIsoCodesAsync();
return allowedCultures.Count == allCultures.Count() ? null : allowedCultures;
}
// If explicit cultures are provided, we should only validate the ones the user has access to.

View File

@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.ContentPublishing;
@@ -165,7 +166,7 @@ internal sealed class ContentPublishingService : IContentPublishingService
return Attempt.FailWithStatus(ContentPublishingOperationStatus.CannotPublishInvariantWhenVariant, new ContentPublishingResult());
}
IEnumerable<string> validCultures = (await _languageService.GetAllAsync()).Select(x => x.IsoCode);
IEnumerable<string> validCultures = await _languageService.GetAllIsoCodesAsync();
if (validCultures.ContainsAll(cultures) is false)
{
scope.Complete();
@@ -488,7 +489,7 @@ internal sealed class ContentPublishingService : IContentPublishingService
return Attempt.Fail(ContentPublishingOperationStatus.CannotPublishVariantWhenNotVariant);
}
var validCultures = (await _languageService.GetAllAsync()).Select(x => x.IsoCode).ToArray();
var validCultures = (await _languageService.GetAllIsoCodesAsync()).ToArray();
foreach (var culture in cultures)
{

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.ContentEditing.Validation;
@@ -137,7 +138,7 @@ internal abstract class ContentValidationServiceBase<TContentType>
return invalidCultures.IsCollectionEmpty();
}
private async Task<string[]> GetCultureCodes() => (await _languageService.GetAllAsync()).Select(language => language.IsoCode).ToArray();
private async Task<string[]> GetCultureCodes() => (await _languageService.GetAllIsoCodesAsync()).ToArray();
private IEnumerable<PropertyValidationError> ValidateProperty(IPropertyType propertyType, PropertyValueModel? propertyValueModel, PropertyValidationContext validationContext)
{

View File

@@ -856,7 +856,7 @@ public class DocumentUrlService : IDocumentUrlService
.Concat(_contentService.GetAncestors(documentIdAttempt.Result).Select(x => x.Key).Reverse());
IEnumerable<ILanguage> languages = await _languageService.GetAllAsync();
var cultures = languages.ToDictionary(x=>x.IsoCode);
var cultures = languages.ToDictionary(x => x.IsoCode);
Guid[] ancestorsOrSelfKeysArray = ancestorsOrSelfKeys as Guid[] ?? ancestorsOrSelfKeys.ToArray();
Dictionary<Guid, Task<ILookup<string, Domain>>> ancestorOrSelfKeyToDomains = ancestorsOrSelfKeysArray

View File

@@ -1,4 +1,4 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
@@ -32,9 +32,9 @@ public interface ILanguageService
Task<string> GetDefaultIsoCodeAsync();
/// <summary>
/// Gets all available languages
/// Gets all available languages.
/// </summary>
/// <returns>An enumerable list of <see cref="ILanguage" /> objects</returns>
/// <returns>An enumerable list of <see cref="ILanguage" /> objects.</returns>
Task<IEnumerable<ILanguage>> GetAllAsync();
/// <summary>
@@ -44,29 +44,29 @@ public interface ILanguageService
Task<IEnumerable<ILanguage>> GetMultipleAsync(IEnumerable<string> isoCodes);
/// <summary>
/// Updates an existing <see cref="ILanguage" /> object
/// Updates an existing <see cref="ILanguage" /> object.
/// </summary>
/// <param name="language"><see cref="ILanguage" /> to update</param>
/// <param name="userKey">Key of the user saving the language</param>
Task<Attempt<ILanguage, LanguageOperationStatus>> UpdateAsync(ILanguage language, Guid userKey);
/// <summary>
/// Creates a new <see cref="ILanguage" /> object
/// Creates a new <see cref="ILanguage" /> object.
/// </summary>
/// <param name="language"><see cref="ILanguage" /> to create</param>
/// <param name="userKey">Key of the user creating the language</param>
/// <param name="language"><see cref="ILanguage" /> to create.</param>
/// <param name="userKey">Key of the user creating the language.</param>
Task<Attempt<ILanguage, LanguageOperationStatus>> CreateAsync(ILanguage language, Guid userKey);
/// <summary>
/// Deletes a <see cref="ILanguage" /> by removing it and its usages from the db
/// Deletes a <see cref="ILanguage" /> by removing it and its usages from the database.
/// </summary>
/// <param name="isoCode">The ISO code of the <see cref="ILanguage" /> to delete</param>
/// <param name="userKey">Key of the user deleting the language</param>
/// <param name="isoCode">The ISO code of the <see cref="ILanguage" /> to delete.</param>
/// <param name="userKey">Key of the user deleting the language.</param>
Task<Attempt<ILanguage?, LanguageOperationStatus>> DeleteAsync(string isoCode, Guid userKey);
/// <summary>
/// Retrieves the isoCodes of configured languages by their Ids
/// Retrieves the ISO codes of configured languages by their Ids.
/// </summary>
/// <param name="ids">The ids of the configured <see cref="ILanguage" />s</param>
/// <returns>The ISO codes of the <see cref="ILanguage" />s</returns>

View File

@@ -1,4 +1,3 @@
using System.Globalization;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;