Skip to content

Commit c64ec51

Browse files
authored
Nonbreaking performance tweaks (#17106)
* Avoid doing multiple lookups in dictionaries, avoid doing string interpolation & adding single char strings to a StringBuilder, made some private/internal classes & some private methods static when possible, use FrozenSet for InvalidFileNameChars * Avoid some array + list allocations & async methods and made some private methods static * Avoid double lookup of XML attribute (and double null check) & avoid an unneeded lookup before writing to a dictionary * Avoid some double lookups # Conflicts: # src/Umbraco.Core/Services/LocalizedTextService.cs * Avoid double lookups # Conflicts: # src/Umbraco.Core/Services/LocalizedTextService.cs * Avoid double lookups * List AsSpan, also to trigger a new build that hopefully goes through * Avoid concatting strings when using writer & more static * Updated CollectionBenchmarks to show that ToArray isn't always the fastest & Lists can be iterated nearly as fast as arrays (and that ToList is nearly as fast as ToArray on IReadOnlyLists in .NET 8) * Fix rebase * Use explicit types ❤️ (I thought it was the other way round...) # Conflicts: # src/Umbraco.Core/Services/LocalizedTextService.cs * Reduce number of lines in HtmlStringUtilities.Truncate to pass code quality analysis * Avoid double lookups & allocating empty arrays * Use native List Find instead of LINQ
1 parent 55b5d7e commit c64ec51

File tree

69 files changed

+443
-442
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+443
-442
lines changed

src/Umbraco.Cms.Api.Delivery/Querying/Filters/ContainsFilterBase.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ public FilterOption BuildFilterOption(string filter)
3737
{
3838
GroupCollection groups = QueryParserRegex.Match(filter).Groups;
3939

40-
if (groups.Count != 3 || groups.ContainsKey("operator") is false || groups.ContainsKey("value") is false)
40+
if (groups.Count != 3 || groups.TryGetValue("operator", out Group? operatorGroup) is false || groups.TryGetValue("value", out Group? valueGroup) is false)
4141
{
4242
return DefaultFilterOption();
4343
}
4444

45-
FilterOperation? filterOperation = ParseFilterOperation(groups["operator"].Value);
45+
FilterOperation? filterOperation = ParseFilterOperation(operatorGroup.Value);
4646
if (filterOperation.HasValue is false)
4747
{
4848
return DefaultFilterOption();
@@ -51,7 +51,7 @@ public FilterOption BuildFilterOption(string filter)
5151
return new FilterOption
5252
{
5353
FieldName = FieldName,
54-
Values = new[] { groups["value"].Value },
54+
Values = [valueGroup.Value],
5555
Operator = filterOperation.Value
5656
};
5757

src/Umbraco.Cms.Api.Delivery/Services/ApiContentQueryProvider.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,14 @@ public PagedModel<Guid> ExecuteQuery(
7979
return new PagedModel<Guid>();
8080
}
8181

82-
Guid[] items = results
83-
.Where(r => r.Values.ContainsKey(ItemIdFieldName))
84-
.Select(r => Guid.Parse(r.Values[ItemIdFieldName]))
85-
.ToArray();
82+
List<Guid> items = [];
83+
foreach (ISearchResult result in results)
84+
{
85+
if (result.Values.TryGetValue(ItemIdFieldName, out string? value))
86+
{
87+
items.Add(Guid.Parse(value));
88+
}
89+
}
8690

8791
return new PagedModel<Guid>(results.TotalItemCount, items);
8892
}

src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private IEnumerable<IPublishedContent> BuildQuery(TemplateQueryExecuteModel mode
9898
if (model.RootDocument?.Id is not null)
9999
{
100100
rootContent = _publishedContentQuery.Content(model.RootDocument.Id);
101-
queryExpression.Append($"Umbraco.Content(Guid.Parse(\"{model.RootDocument.Id}\"))");
101+
queryExpression.Append("Umbraco.Content(Guid.Parse(\"").Append(model.RootDocument.Id).Append("\"))");
102102
}
103103
else
104104
{
@@ -115,7 +115,7 @@ private IEnumerable<IPublishedContent> GetRootContentQuery(TemplateQueryExecuteM
115115

116116
if (model.DocumentTypeAlias.IsNullOrWhiteSpace() == false)
117117
{
118-
queryExpression.Append($".ChildrenOfType(\"{model.DocumentTypeAlias}\")");
118+
queryExpression.Append(".ChildrenOfType(\"").Append(model.DocumentTypeAlias).Append("\")");
119119
return rootContent == null
120120
? Enumerable.Empty<IPublishedContent>()
121121
: rootContent.ChildrenOfType(_variationContextAccessor, _contentCache, _documentNavigationQueryService, model.DocumentTypeAlias);
@@ -176,7 +176,7 @@ string PropertyModelType(TemplateQueryPropertyType templateQueryPropertyType)
176176
//for review - this uses a tonized query rather then the normal linq query.
177177
contentQuery = contentQuery.Where(operation.Compile());
178178
queryExpression.Append(_indent);
179-
queryExpression.Append($".Where({operation})");
179+
queryExpression.Append(".Where(").Append(operation).Append(')');
180180
}
181181

182182
return contentQuery;
@@ -220,7 +220,7 @@ private IEnumerable<IPublishedContent> ApplySorting(TemplateQueryExecuteSortMode
220220
return contentQuery;
221221
}
222222

223-
private IEnumerable<IPublishedContent> ApplyPaging(int take, IEnumerable<IPublishedContent> contentQuery, StringBuilder queryExpression)
223+
private static IEnumerable<IPublishedContent> ApplyPaging(int take, IEnumerable<IPublishedContent> contentQuery, StringBuilder queryExpression)
224224
{
225225
if (take <= 0)
226226
{
@@ -229,7 +229,7 @@ private IEnumerable<IPublishedContent> ApplyPaging(int take, IEnumerable<IPublis
229229

230230
contentQuery = contentQuery.Take(take);
231231
queryExpression.Append(_indent);
232-
queryExpression.Append($".Take({take})");
232+
queryExpression.Append(".Take(").Append(take).Append(')');
233233

234234
return contentQuery;
235235
}

src/Umbraco.Cms.Api.Management/Mapping/ContentType/ContentTypeMapDefinition.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ protected IEnumerable<TPropertyTypeModel> MapPropertyTypes(TContentType source)
2525
{
2626
Id = propertyType.Key,
2727
SortOrder = propertyType.SortOrder,
28-
Container = groupKeysByPropertyKeys.ContainsKey(propertyType.Key)
29-
? new ReferenceByIdModel(groupKeysByPropertyKeys[propertyType.Key])
28+
Container = groupKeysByPropertyKeys.TryGetValue(propertyType.Key, out Guid groupKey)
29+
? new ReferenceByIdModel(groupKey)
3030
: null,
3131
Name = propertyType.Name,
3232
Alias = propertyType.Alias,

src/Umbraco.Cms.Api.Management/Services/ConflictingRouteService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Reflection;
2+
using System.Runtime.InteropServices;
23
using Umbraco.Cms.Api.Management.Controllers;
34
using Umbraco.Cms.Core.Composing;
45
using Umbraco.Cms.Core.Services;
@@ -19,7 +20,7 @@ public class ConflictingRouteService : IConflictingRouteService
1920
public bool HasConflictingRoutes(out string controllerName)
2021
{
2122
var controllers = _typeLoader.GetTypes<ManagementApiControllerBase>().ToList();
22-
foreach (Type controller in controllers)
23+
foreach (Type controller in CollectionsMarshal.AsSpan(controllers))
2324
{
2425
Type[] potentialConflicting = controllers.Where(x => x.Name == controller.Name).ToArray();
2526
if (potentialConflicting.Length > 1)

src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace Umbraco.Cms.Persistence.EFCore.Locking;
1515

16-
internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
16+
internal sealed class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
1717
where T : DbContext
1818
{
1919
private ConnectionStrings _connectionStrings;
@@ -58,7 +58,7 @@ public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null
5858
return new SqlServerDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
5959
}
6060

61-
private class SqlServerDistributedLock : IDistributedLock
61+
private sealed class SqlServerDistributedLock : IDistributedLock
6262
{
6363
private readonly SqlServerEFCoreDistributedLockingMechanism<T> _parent;
6464
private readonly TimeSpan _timeout;

src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace Umbraco.Cms.Persistence.EFCore.Locking;
1515

16-
internal class SqliteEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
16+
internal sealed class SqliteEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
1717
where T : DbContext
1818
{
1919
private ConnectionStrings _connectionStrings;
@@ -55,7 +55,7 @@ public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null
5555
return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
5656
}
5757

58-
private class SqliteDistributedLock : IDistributedLock
58+
private sealed class SqliteDistributedLock : IDistributedLock
5959
{
6060
private readonly SqliteEFCoreDistributedLockingMechanism<T> _parent;
6161
private readonly TimeSpan _timeout;
@@ -164,7 +164,7 @@ private void ObtainWriteLock()
164164
});
165165
}
166166

167-
private bool IsBusyOrLocked(SqliteException ex) =>
167+
private static bool IsBusyOrLocked(SqliteException ex) =>
168168
ex.SqliteErrorCode
169169
is raw.SQLITE_BUSY
170170
or raw.SQLITE_LOCKED

src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreDetachableScope.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Umbraco.Cms.Persistence.EFCore.Scoping;
99

10-
internal class EFCoreDetachableScope<TDbContext> : EFCoreScope<TDbContext> where TDbContext : DbContext
10+
internal sealed class EFCoreDetachableScope<TDbContext> : EFCoreScope<TDbContext> where TDbContext : DbContext
1111
{
1212
private readonly IEFCoreScopeAccessor<TDbContext> _efCoreScopeAccessor;
1313
private readonly EFCoreScopeProvider<TDbContext> _efCoreScopeProvider;

src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeAccessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Umbraco.Cms.Persistence.EFCore.Scoping;
44

5-
internal class EFCoreScopeAccessor<TDbContext> : IEFCoreScopeAccessor<TDbContext> where TDbContext : DbContext
5+
internal sealed class EFCoreScopeAccessor<TDbContext> : IEFCoreScopeAccessor<TDbContext> where TDbContext : DbContext
66
{
77
private readonly IAmbientEFCoreScopeStack<TDbContext> _ambientEfCoreScopeStack;
88

src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace Umbraco.Cms.Persistence.EFCore.Scoping;
1515

16-
internal class EFCoreScopeProvider<TDbContext> : IEFCoreScopeProvider<TDbContext> where TDbContext : DbContext
16+
internal sealed class EFCoreScopeProvider<TDbContext> : IEFCoreScopeProvider<TDbContext> where TDbContext : DbContext
1717
{
1818
private readonly IAmbientEFCoreScopeStack<TDbContext> _ambientEfCoreScopeStack;
1919
private readonly ILoggerFactory _loggerFactory;

0 commit comments

Comments
 (0)