diff --git a/src/Umbraco.Cms.Api.Delivery/Querying/Filters/ContainsFilterBase.cs b/src/Umbraco.Cms.Api.Delivery/Querying/Filters/ContainsFilterBase.cs index 1770bfd4b9..389246534c 100644 --- a/src/Umbraco.Cms.Api.Delivery/Querying/Filters/ContainsFilterBase.cs +++ b/src/Umbraco.Cms.Api.Delivery/Querying/Filters/ContainsFilterBase.cs @@ -37,12 +37,12 @@ public abstract class ContainsFilterBase : IFilterHandler { GroupCollection groups = QueryParserRegex.Match(filter).Groups; - if (groups.Count != 3 || groups.ContainsKey("operator") is false || groups.ContainsKey("value") is false) + if (groups.Count != 3 || groups.TryGetValue("operator", out Group? operatorGroup) is false || groups.TryGetValue("value", out Group? valueGroup) is false) { return DefaultFilterOption(); } - FilterOperation? filterOperation = ParseFilterOperation(groups["operator"].Value); + FilterOperation? filterOperation = ParseFilterOperation(operatorGroup.Value); if (filterOperation.HasValue is false) { return DefaultFilterOption(); @@ -51,7 +51,7 @@ public abstract class ContainsFilterBase : IFilterHandler return new FilterOption { FieldName = FieldName, - Values = new[] { groups["value"].Value }, + Values = [valueGroup.Value], Operator = filterOperation.Value }; diff --git a/src/Umbraco.Cms.Api.Delivery/Services/ApiContentQueryProvider.cs b/src/Umbraco.Cms.Api.Delivery/Services/ApiContentQueryProvider.cs index 566a5da096..07453476f6 100644 --- a/src/Umbraco.Cms.Api.Delivery/Services/ApiContentQueryProvider.cs +++ b/src/Umbraco.Cms.Api.Delivery/Services/ApiContentQueryProvider.cs @@ -79,10 +79,14 @@ internal sealed class ApiContentQueryProvider : IApiContentQueryProvider return new PagedModel(); } - Guid[] items = results - .Where(r => r.Values.ContainsKey(ItemIdFieldName)) - .Select(r => Guid.Parse(r.Values[ItemIdFieldName])) - .ToArray(); + List items = []; + foreach (ISearchResult result in results) + { + if (result.Values.TryGetValue(ItemIdFieldName, out string? value)) + { + items.Add(Guid.Parse(value)); + } + } return new PagedModel(results.TotalItemCount, items); } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs index c7aa2189c1..3f22c3931c 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs @@ -98,7 +98,7 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase if (model.RootDocument?.Id is not null) { rootContent = _publishedContentQuery.Content(model.RootDocument.Id); - queryExpression.Append($"Umbraco.Content(Guid.Parse(\"{model.RootDocument.Id}\"))"); + queryExpression.Append("Umbraco.Content(Guid.Parse(\"").Append(model.RootDocument.Id).Append("\"))"); } else { @@ -115,7 +115,7 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase if (model.DocumentTypeAlias.IsNullOrWhiteSpace() == false) { - queryExpression.Append($".ChildrenOfType(\"{model.DocumentTypeAlias}\")"); + queryExpression.Append(".ChildrenOfType(\"").Append(model.DocumentTypeAlias).Append("\")"); return rootContent == null ? Enumerable.Empty() : rootContent.ChildrenOfType(_variationContextAccessor, _contentCache, _documentNavigationQueryService, model.DocumentTypeAlias); @@ -176,7 +176,7 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase //for review - this uses a tonized query rather then the normal linq query. contentQuery = contentQuery.Where(operation.Compile()); queryExpression.Append(_indent); - queryExpression.Append($".Where({operation})"); + queryExpression.Append(".Where(").Append(operation).Append(')'); } return contentQuery; @@ -220,7 +220,7 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase return contentQuery; } - private IEnumerable ApplyPaging(int take, IEnumerable contentQuery, StringBuilder queryExpression) + private static IEnumerable ApplyPaging(int take, IEnumerable contentQuery, StringBuilder queryExpression) { if (take <= 0) { @@ -229,7 +229,7 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase contentQuery = contentQuery.Take(take); queryExpression.Append(_indent); - queryExpression.Append($".Take({take})"); + queryExpression.Append(".Take(").Append(take).Append(')'); return contentQuery; } diff --git a/src/Umbraco.Cms.Api.Management/Mapping/ContentType/ContentTypeMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/ContentType/ContentTypeMapDefinition.cs index d77014843c..7cc15e2a76 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/ContentType/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/ContentType/ContentTypeMapDefinition.cs @@ -25,8 +25,8 @@ public abstract class ContentTypeMapDefinition().ToList(); - foreach (Type controller in controllers) + foreach (Type controller in CollectionsMarshal.AsSpan(controllers)) { Type[] potentialConflicting = controllers.Where(x => x.Name == controller.Name).ToArray(); if (potentialConflicting.Length > 1) diff --git a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs index 91342c386f..8e493ecffb 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqlServerEFCoreDistributedLockingMechanism.cs @@ -13,7 +13,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Persistence.EFCore.Locking; -internal class SqlServerEFCoreDistributedLockingMechanism : IDistributedLockingMechanism +internal sealed class SqlServerEFCoreDistributedLockingMechanism : IDistributedLockingMechanism where T : DbContext { private ConnectionStrings _connectionStrings; @@ -58,7 +58,7 @@ internal class SqlServerEFCoreDistributedLockingMechanism : IDistributedLocki return new SqlServerDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value); } - private class SqlServerDistributedLock : IDistributedLock + private sealed class SqlServerDistributedLock : IDistributedLock { private readonly SqlServerEFCoreDistributedLockingMechanism _parent; private readonly TimeSpan _timeout; diff --git a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs index 8308ae4d3a..b6e2fa0b7b 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs @@ -13,7 +13,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Persistence.EFCore.Locking; -internal class SqliteEFCoreDistributedLockingMechanism : IDistributedLockingMechanism +internal sealed class SqliteEFCoreDistributedLockingMechanism : IDistributedLockingMechanism where T : DbContext { private ConnectionStrings _connectionStrings; @@ -55,7 +55,7 @@ internal class SqliteEFCoreDistributedLockingMechanism : IDistributedLockingM return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value); } - private class SqliteDistributedLock : IDistributedLock + private sealed class SqliteDistributedLock : IDistributedLock { private readonly SqliteEFCoreDistributedLockingMechanism _parent; private readonly TimeSpan _timeout; @@ -164,7 +164,7 @@ internal class SqliteEFCoreDistributedLockingMechanism : IDistributedLockingM }); } - private bool IsBusyOrLocked(SqliteException ex) => + private static bool IsBusyOrLocked(SqliteException ex) => ex.SqliteErrorCode is raw.SQLITE_BUSY or raw.SQLITE_LOCKED diff --git a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreDetachableScope.cs b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreDetachableScope.cs index e23a830e3f..0a3b5352de 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreDetachableScope.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreDetachableScope.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Scoping; namespace Umbraco.Cms.Persistence.EFCore.Scoping; -internal class EFCoreDetachableScope : EFCoreScope where TDbContext : DbContext +internal sealed class EFCoreDetachableScope : EFCoreScope where TDbContext : DbContext { private readonly IEFCoreScopeAccessor _efCoreScopeAccessor; private readonly EFCoreScopeProvider _efCoreScopeProvider; diff --git a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeAccessor.cs b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeAccessor.cs index 098a6957c4..f9358e3c3c 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeAccessor.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeAccessor.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore; namespace Umbraco.Cms.Persistence.EFCore.Scoping; -internal class EFCoreScopeAccessor : IEFCoreScopeAccessor where TDbContext : DbContext +internal sealed class EFCoreScopeAccessor : IEFCoreScopeAccessor where TDbContext : DbContext { private readonly IAmbientEFCoreScopeStack _ambientEfCoreScopeStack; diff --git a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeProvider.cs b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeProvider.cs index 9e41eedb3c..310857df9a 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeProvider.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScopeProvider.cs @@ -13,7 +13,7 @@ using IScopeProvider = Umbraco.Cms.Infrastructure.Scoping.IScopeProvider; namespace Umbraco.Cms.Persistence.EFCore.Scoping; -internal class EFCoreScopeProvider : IEFCoreScopeProvider where TDbContext : DbContext +internal sealed class EFCoreScopeProvider : IEFCoreScopeProvider where TDbContext : DbContext { private readonly IAmbientEFCoreScopeStack _ambientEfCoreScopeStack; private readonly ILoggerFactory _loggerFactory; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ColumnInSchemaDto.cs b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ColumnInSchemaDto.cs index 0c09f87d51..38e6c34e1d 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ColumnInSchemaDto.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ColumnInSchemaDto.cs @@ -2,7 +2,7 @@ using NPoco; namespace Umbraco.Cms.Persistence.SqlServer.Dtos; -internal class ColumnInSchemaDto +internal sealed class ColumnInSchemaDto { [Column("TABLE_NAME")] public string TableName { get; set; } = null!; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerColumnDto.cs b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerColumnDto.cs index b0299a489d..ee1020fb0b 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerColumnDto.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerColumnDto.cs @@ -2,7 +2,7 @@ using NPoco; namespace Umbraco.Cms.Persistence.SqlServer.Dtos; -internal class ConstraintPerColumnDto +internal sealed class ConstraintPerColumnDto { [Column("TABLE_NAME")] public string TableName { get; set; } = null!; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerTableDto.cs b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerTableDto.cs index fe87ef2909..ccc37c6756 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerTableDto.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/ConstraintPerTableDto.cs @@ -2,7 +2,7 @@ using NPoco; namespace Umbraco.Cms.Persistence.SqlServer.Dtos; -internal class ConstraintPerTableDto +internal sealed class ConstraintPerTableDto { [Column("TABLE_NAME")] public string TableName { get; set; } = null!; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefaultConstraintPerColumnDto.cs b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefaultConstraintPerColumnDto.cs index a1bde415a3..c5202fe547 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefaultConstraintPerColumnDto.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefaultConstraintPerColumnDto.cs @@ -2,7 +2,7 @@ using NPoco; namespace Umbraco.Cms.Persistence.SqlServer.Dtos; -internal class DefaultConstraintPerColumnDto +internal sealed class DefaultConstraintPerColumnDto { [Column("TABLE_NAME")] public string TableName { get; set; } = null!; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefinedIndexDto.cs b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefinedIndexDto.cs index e85d91f1dd..8a7a2b2479 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefinedIndexDto.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Dtos/DefinedIndexDto.cs @@ -2,7 +2,7 @@ using NPoco; namespace Umbraco.Cms.Persistence.SqlServer.Dtos; -internal class DefinedIndexDto +internal sealed class DefinedIndexDto { [Column("TABLE_NAME")] public string TableName { get; set; } = null!; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/LocalDb.cs b/src/Umbraco.Cms.Persistence.SqlServer/LocalDb.cs index a8a6480c89..56fd3285a8 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/LocalDb.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/LocalDb.cs @@ -821,7 +821,7 @@ public class LocalDb /// The LDF logical name. /// The MDF filename. /// The LDF filename. - private void GetFilenames( + private static void GetFilenames( SqlCommand cmd, string databaseName, out string? mdfName, diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/MicrosoftSqlSyntaxProviderBase.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/MicrosoftSqlSyntaxProviderBase.cs index 7256317c15..bc1a7afc6b 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/MicrosoftSqlSyntaxProviderBase.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/MicrosoftSqlSyntaxProviderBase.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; using NPoco; using Umbraco.Cms.Core; @@ -36,7 +37,7 @@ public abstract class MicrosoftSqlSyntaxProviderBase : SqlSyntaxProvide public override string GetQuotedTableName(string? tableName) { - if (tableName?.Contains(".") == false) + if (tableName?.Contains('.') == false) { return $"[{tableName}]"; } @@ -207,14 +208,14 @@ public abstract class MicrosoftSqlSyntaxProviderBase : SqlSyntaxProvide List indexSql = Format(tableDefinition.Indexes); //Loop through index statements and execute sql - foreach (var sql in indexSql) + foreach (var sql in CollectionsMarshal.AsSpan(indexSql)) { _logger.LogInformation("Create Index:\n {Sql}", sql); database.Execute(new Sql(sql)); } //Loop through foreignkey statements and execute sql - foreach (var sql in foreignSql) + foreach (var sql in CollectionsMarshal.AsSpan(foreignSql)) { _logger.LogInformation("Create Foreign Key:\n {Sql}", sql); database.Execute(new Sql(sql)); diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/PocoDataDataReader.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/PocoDataDataReader.cs index 2b9d35b959..eeed69a8b7 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/PocoDataDataReader.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/PocoDataDataReader.cs @@ -19,7 +19,7 @@ namespace Umbraco.Cms.Persistence.SqlServer.Services; /// this /// reader. /// -internal class PocoDataDataReader : BulkDataReader +internal sealed class PocoDataDataReader : BulkDataReader where TSyntax : ISqlSyntaxProvider { private readonly ColumnDefinition[] _columnDefinitions; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlAzureDatabaseProviderMetadata.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlAzureDatabaseProviderMetadata.cs index 95dda5fe7b..746ad2b93e 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlAzureDatabaseProviderMetadata.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlAzureDatabaseProviderMetadata.cs @@ -83,20 +83,20 @@ public class SqlAzureDatabaseProviderMetadata : IDatabaseProviderMetadata var user = databaseModel.Login; var password = databaseModel.Password; - if (server.Contains(".") && ServerStartsWithTcp(server) == false) + if (server.Contains('.') && ServerStartsWithTcp(server) == false) { server = $"tcp:{server}"; } - if (server.Contains(".") == false && ServerStartsWithTcp(server)) + if (server.Contains('.') == false && ServerStartsWithTcp(server)) { - var serverName = server.Contains(",") + var serverName = server.Contains(',') ? server.Substring(0, server.IndexOf(",", StringComparison.Ordinal)) : server; var portAddition = string.Empty; - if (server.Contains(",")) + if (server.Contains(',')) { portAddition = server.Substring(server.IndexOf(",", StringComparison.Ordinal)); } @@ -109,12 +109,12 @@ public class SqlAzureDatabaseProviderMetadata : IDatabaseProviderMetadata server = $"tcp:{server}.database.windows.net"; } - if (server.Contains(",") == false) + if (server.Contains(',') == false) { server = $"{server},1433"; } - if (user?.Contains("@") == false) + if (user?.Contains('@') == false) { var userDomain = server; @@ -123,7 +123,7 @@ public class SqlAzureDatabaseProviderMetadata : IDatabaseProviderMetadata userDomain = userDomain.Substring(userDomain.IndexOf(":", StringComparison.Ordinal) + 1); } - if (userDomain.Contains(".")) + if (userDomain.Contains('.')) { userDomain = userDomain.Substring(0, userDomain.IndexOf(".", StringComparison.Ordinal)); } diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerBulkSqlInsertProvider.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerBulkSqlInsertProvider.cs index d87fa99d5e..c6ae402147 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerBulkSqlInsertProvider.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerBulkSqlInsertProvider.cs @@ -39,7 +39,7 @@ public class SqlServerBulkSqlInsertProvider : IBulkSqlInsertProvider /// The PocoData object corresponding to the record's type. /// The records. /// The number of records that were inserted. - private int BulkInsertRecordsSqlServer(IUmbracoDatabase database, PocoData pocoData, IEnumerable records) + private static int BulkInsertRecordsSqlServer(IUmbracoDatabase database, PocoData pocoData, IEnumerable records) { // TODO: The main reason this exists is because the NPoco InsertBulk method doesn't return the number of items. // It is worth investigating the performance of this vs NPoco's because we use a custom BulkDataReader diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs index 63e058df6c..a6919521b1 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs @@ -82,7 +82,7 @@ public class SqlServerDatabaseProviderMetadata : IDatabaseProviderMetadata return connectionString; } - private string HandleIntegratedAuthentication(string connectionString, DatabaseModel databaseModel) + private static string HandleIntegratedAuthentication(string connectionString, DatabaseModel databaseModel) { if (databaseModel.IntegratedAuth) { @@ -96,7 +96,7 @@ public class SqlServerDatabaseProviderMetadata : IDatabaseProviderMetadata return connectionString; } - private string HandleTrustServerCertificate(string connectionString, DatabaseModel databaseModel) + private static string HandleTrustServerCertificate(string connectionString, DatabaseModel databaseModel) { if (databaseModel.TrustServerCertificate) { diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs index a7f183e57a..d0ffd7ee4f 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDistributedLockingMechanism.cs @@ -58,7 +58,7 @@ public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism return new SqlServerDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value); } - private class SqlServerDistributedLock : IDistributedLock + private sealed class SqlServerDistributedLock : IDistributedLock { private readonly SqlServerDistributedLockingMechanism _parent; private readonly TimeSpan _timeout; diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs index 511826114d..87d736ac34 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs @@ -438,7 +438,7 @@ where tbl.[name]=@0 and col.[name]=@1;", private static SqlInspectionUtilities SqlInspector => _sqlInspector ??= new SqlInspectionUtilities(); - private class SqlInspectionUtilities + private sealed class SqlInspectionUtilities { private readonly Func _getSqlRhs; private readonly Func _getSqlText; @@ -463,7 +463,7 @@ _sqlInspector ??= new SqlInspectionUtilities(); #endregion - private class SqlPrimaryKey + private sealed class SqlPrimaryKey { public string Name { get; set; } = null!; } diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteBulkSqlInsertProvider.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteBulkSqlInsertProvider.cs index ff0a64a74b..412111a2a8 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteBulkSqlInsertProvider.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteBulkSqlInsertProvider.cs @@ -35,7 +35,7 @@ public class SqliteBulkSqlInsertProvider : IBulkSqlInsertProvider /// The PocoData object corresponding to the record's type. /// The records. /// The number of records that were inserted. - private int BulkInsertRecordsSqlite(IUmbracoDatabase database, PocoData pocoData, IEnumerable records) + private static int BulkInsertRecordsSqlite(IUmbracoDatabase database, PocoData pocoData, IEnumerable records) { var count = 0; var inTrans = database.InTransaction; diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs index f43a1eff05..d954761a28 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs @@ -53,7 +53,7 @@ public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value); } - private class SqliteDistributedLock : IDistributedLock + private sealed class SqliteDistributedLock : IDistributedLock { private readonly SqliteDistributedLockingMechanism _parent; private readonly TimeSpan _timeout; diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqlitePreferDeferredTransactionsConnection.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqlitePreferDeferredTransactionsConnection.cs index 7c5a9f56f8..a8064c4e81 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqlitePreferDeferredTransactionsConnection.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqlitePreferDeferredTransactionsConnection.cs @@ -45,7 +45,7 @@ public class SqlitePreferDeferredTransactionsConnection : DbConnection protected override DbCommand CreateDbCommand() => new CommandWrapper(_inner.CreateCommand()); - private class CommandWrapper : DbCommand + private sealed class CommandWrapper : DbCommand { private readonly DbCommand _inner; diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs index 24d4162da3..2ed07cbf02 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs @@ -2,6 +2,7 @@ using System.Data; using System.Data.Common; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; @@ -102,7 +103,7 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase sb.AppendLine($", {primaryKey}"); } - foreach (var foreignKey in foreignKeys) + foreach (var foreignKey in CollectionsMarshal.AsSpan(foreignKeys)) { sb.AppendLine($", {foreignKey}"); } @@ -303,7 +304,7 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase if (!skipKeysAndIndexes) { - foreach (var foreignKey in foreignKeys) + foreach (var foreignKey in CollectionsMarshal.AsSpan(foreignKeys)) { sb.AppendLine($", {foreignKey}"); } @@ -434,7 +435,7 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase public override IDictionary ScalarMappers => _scalarMappers; - private class Constraint + private sealed class Constraint { public string TableName { get; } @@ -452,7 +453,7 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase public override string ToString() => ConstraintName; } - private class SqliteMaster + private sealed class SqliteMaster { public string Type { get; set; } = null!; @@ -461,7 +462,7 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase public string Sql { get; set; } = null!; } - private class IndexMeta + private sealed class IndexMeta { public string TableName { get; set; } = null!; diff --git a/src/Umbraco.Core/Collections/ObservableDictionary.cs b/src/Umbraco.Core/Collections/ObservableDictionary.cs index 8d920bbe98..16683b3dfc 100644 --- a/src/Umbraco.Core/Collections/ObservableDictionary.cs +++ b/src/Umbraco.Core/Collections/ObservableDictionary.cs @@ -39,12 +39,12 @@ public class ObservableDictionary : ObservableCollection, public bool Remove(TKey key) { - if (!Indecies.ContainsKey(key)) + if (!Indecies.TryGetValue(key, out int index)) { return false; } - RemoveAt(Indecies[key]); + RemoveAt(index); return true; } @@ -72,13 +72,13 @@ public class ObservableDictionary : ObservableCollection, throw new InvalidOperationException("Key of new value does not match."); } - if (!Indecies.ContainsKey(key)) + if (!Indecies.TryGetValue(key, out int index)) { Add(value); } else { - this[Indecies[key]] = value; + this[index] = value; } } } @@ -97,7 +97,7 @@ public class ObservableDictionary : ObservableCollection, /// False if key not found public bool Replace(TKey key, TValue value) { - if (!Indecies.ContainsKey(key)) + if (!Indecies.TryGetValue(key, out int index)) { return false; } @@ -108,7 +108,7 @@ public class ObservableDictionary : ObservableCollection, throw new InvalidOperationException("Key of new value does not match."); } - this[Indecies[key]] = value; + this[index] = value; return true; } @@ -134,7 +134,7 @@ public class ObservableDictionary : ObservableCollection, /// public void ChangeKey(TKey currentKey, TKey newKey) { - if (!Indecies.ContainsKey(currentKey)) + if (!Indecies.TryGetValue(currentKey, out int currentIndex)) { throw new InvalidOperationException($"No item with the key '{currentKey}' was found in the dictionary."); } @@ -144,8 +144,6 @@ public class ObservableDictionary : ObservableCollection, throw new ArgumentException($"An element with the same key '{newKey}' already exists in the dictionary.", nameof(newKey)); } - var currentIndex = Indecies[currentKey]; - Indecies.Remove(currentKey); Indecies.Add(newKey, currentIndex); } diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs index 7ad290a192..11d5b9a857 100644 --- a/src/Umbraco.Core/Composing/ReferenceResolver.cs +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Reflection; +using System.Runtime.InteropServices; using System.Security; using Microsoft.Extensions.Logging; @@ -12,7 +13,7 @@ namespace Umbraco.Cms.Core.Composing; /// Borrowed and modified from /// https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs /// -internal class ReferenceResolver +internal sealed class ReferenceResolver { private readonly IReadOnlyList _assemblies; private readonly Dictionary _classifications; @@ -33,7 +34,7 @@ internal class ReferenceResolver } } - protected enum Classification + private enum Classification { Unknown, DoesNotReferenceUmbraco, @@ -59,7 +60,7 @@ internal class ReferenceResolver // Load in each assembly in the directory of the entry assembly to be included in the search // for Umbraco dependencies/transitive dependencies - foreach (var dir in assemblyLocations) + foreach (var dir in CollectionsMarshal.AsSpan(assemblyLocations)) { foreach (var dll in Directory.EnumerateFiles(dir ?? string.Empty, "*.dll")) { @@ -118,7 +119,7 @@ internal class ReferenceResolver return applicationParts; } - protected virtual IEnumerable GetReferences(Assembly assembly) + private IEnumerable GetReferences(Assembly assembly) { foreach (AssemblyName referenceName in assembly.GetReferencedAssemblies()) { @@ -142,11 +143,11 @@ internal class ReferenceResolver } } - private IEnumerable GetAssemblyFolders(IEnumerable assemblies) => + private static IEnumerable GetAssemblyFolders(IEnumerable assemblies) => assemblies.Select(x => Path.GetDirectoryName(GetAssemblyLocation(x))).Distinct(); // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs - private string GetAssemblyLocation(Assembly assembly) + private static string GetAssemblyLocation(Assembly assembly) { if (Uri.TryCreate(assembly.Location, UriKind.Absolute, out Uri? result) && result.IsFile && string.IsNullOrWhiteSpace(result.Fragment)) diff --git a/src/Umbraco.Core/Composing/TypeHelper.cs b/src/Umbraco.Core/Composing/TypeHelper.cs index 6cb5426f77..5fa4b2b57b 100644 --- a/src/Umbraco.Core/Composing/TypeHelper.cs +++ b/src/Umbraco.Core/Composing/TypeHelper.cs @@ -17,7 +17,7 @@ public static class TypeHelper private static readonly ConcurrentDictionary GetFieldsCache = new(); - private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; + private static readonly Assembly[] EmptyAssemblies = []; /// /// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also @@ -376,10 +376,10 @@ public static class TypeHelper if (contract.IsGenericParameter) { // eg - if (bindings.ContainsKey(contract.Name)) + if (bindings.TryGetValue(contract.Name, out Type? binding)) { // already bound: ensure it's compatible - return bindings[contract.Name] == implementation; + return binding == implementation; } // not already bound: bind diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 21b895bf6f..3c4fa06d54 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -342,11 +342,7 @@ public sealed class TypeLoader // if we are to cache the results, do so if (cache) { - var added = _types.ContainsKey(listKey) == false; - if (added) - { - _types[listKey] = typeList; - } + var added = _types.TryAdd(listKey, typeList); if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) { _logger.LogDebug("Got {TypeName}, caching ({CacheType}).", GetName(baseType, attributeType), added.ToString().ToLowerInvariant()); diff --git a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs index 56b714d35a..60bf73d478 100644 --- a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs @@ -144,9 +144,9 @@ public abstract class WeightedCollectionBuilderBase() diff --git a/src/Umbraco.Core/Editors/UserEditorAuthorizationHelper.cs b/src/Umbraco.Core/Editors/UserEditorAuthorizationHelper.cs index be9b05230f..eb1e67da9d 100644 --- a/src/Umbraco.Core/Editors/UserEditorAuthorizationHelper.cs +++ b/src/Umbraco.Core/Editors/UserEditorAuthorizationHelper.cs @@ -85,7 +85,7 @@ public class UserEditorAuthorizationHelper { var savingGroupAliases = userGroupAliases.ToArray(); var existingGroupAliases = savingUser == null - ? new string[0] + ? [] : savingUser.Groups.Select(x => x.Alias).ToArray(); IEnumerable addedGroupAliases = savingGroupAliases.Except(existingGroupAliases); diff --git a/src/Umbraco.Core/Enum.cs b/src/Umbraco.Core/Enum.cs index 03dd0d51bc..63e4db17f9 100644 --- a/src/Umbraco.Core/Enum.cs +++ b/src/Umbraco.Core/Enum.cs @@ -45,7 +45,7 @@ public static class Enum public static string[] GetNames() => ValueToName.Values.ToArray(); - public static string? GetName(T value) => ValueToName.TryGetValue(value, out var name) ? name : null; + public static string? GetName(T value) => ValueToName.GetValueOrDefault(value); public static T Parse(string value, bool ignoreCase = false) { diff --git a/src/Umbraco.Core/Extensions/DictionaryExtensions.cs b/src/Umbraco.Core/Extensions/DictionaryExtensions.cs index ca224a0f61..db3a01cee5 100644 --- a/src/Umbraco.Core/Extensions/DictionaryExtensions.cs +++ b/src/Umbraco.Core/Extensions/DictionaryExtensions.cs @@ -27,12 +27,14 @@ public static class DictionaryExtensions public static TVal GetOrCreate(this IDictionary dict, TKey key) where TVal : class, new() { - if (dict.ContainsKey(key) == false) + if (dict.TryGetValue(key, out TVal? existing)) { - dict.Add(key, new TVal()); + return existing; } - return dict[key]; + TVal value = new(); + dict.Add(key, value); + return value; } /// @@ -204,9 +206,9 @@ public static class DictionaryExtensions /// public static TVal? GetValue(this IDictionary d, TKey key, TVal? defaultValue = default) { - if (d.ContainsKey(key)) + if (d.TryGetValue(key, out TVal? value)) { - return d[key]; + return value; } return defaultValue; @@ -220,7 +222,7 @@ public static class DictionaryExtensions /// /// public static string? GetValueAsString(this IDictionary d, TKey key) - => d.ContainsKey(key) ? d[key]!.ToString() : string.Empty; + => d.TryGetValue(key, out TVal? value) ? value!.ToString() : string.Empty; /// /// Returns the value of the key value based on the key as it's string value, if the key is not found or is an empty @@ -232,9 +234,9 @@ public static class DictionaryExtensions /// public static string? GetValueAsString(this IDictionary d, TKey key, string defaultValue) { - if (d.ContainsKey(key)) + if (d.TryGetValue(key, out TVal? dictionaryValue)) { - var value = d[key]!.ToString(); + var value = dictionaryValue!.ToString(); if (value != string.Empty) { return value; @@ -268,7 +270,13 @@ public static class DictionaryExtensions var builder = new StringBuilder(); foreach (KeyValuePair i in d) { - builder.Append(string.Format("{0}={1}&", WebUtility.UrlEncode(i.Key), i.Value == null ? string.Empty : WebUtility.UrlEncode(i.Value.ToString()))); + builder.Append(WebUtility.UrlEncode(i.Key)); + builder.Append('='); + if (i.Value != null) + { + builder.Append(WebUtility.UrlEncode(i.Value.ToString())); + } + builder.Append('&'); } return builder.ToString().TrimEnd(Constants.CharArrays.Ampersand); diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index 0200d929a7..d77634f909 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -3449,9 +3449,9 @@ public static class PublishedContentExtensions { "Url", "Url" }, }; - foreach (KeyValuePair field in stdFields.Where(x => fields.ContainsKey(x.Key) == false)) + foreach (KeyValuePair field in stdFields) { - fields[field.Key] = field.Value; + fields.TryAdd(field.Key, field.Value); } return fields; diff --git a/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs b/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs index 383ab7f5bf..b49a8d9a2e 100644 --- a/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs +++ b/src/Umbraco.Core/Extensions/RequestHandlerSettingsExtension.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using Microsoft.Extensions.Configuration; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; @@ -63,9 +64,9 @@ public static class RequestHandlerSettingsExtension var priorityReplacementsList = priorityReplacements.ToList(); var alternativeReplacementsList = alternativeReplacements.ToList(); - foreach (CharItem alternativeReplacement in alternativeReplacementsList) + foreach (CharItem alternativeReplacement in CollectionsMarshal.AsSpan(alternativeReplacementsList)) { - foreach (CharItem priorityReplacement in priorityReplacementsList) + foreach (CharItem priorityReplacement in CollectionsMarshal.AsSpan(priorityReplacementsList)) { if (priorityReplacement.Char == alternativeReplacement.Char) { diff --git a/src/Umbraco.Core/Extensions/XmlExtensions.cs b/src/Umbraco.Core/Extensions/XmlExtensions.cs index 4a9d9a6fdf..e879907592 100644 --- a/src/Umbraco.Core/Extensions/XmlExtensions.cs +++ b/src/Umbraco.Core/Extensions/XmlExtensions.cs @@ -110,18 +110,13 @@ public static class XmlExtensions throw new ArgumentNullException("xml"); } - if (xml.HasAttributes == false) + XAttribute? xmlAttribute = xml.Attribute(attributeName); + if (xmlAttribute == null) { return default; } - if (xml.Attribute(attributeName) == null) - { - return default; - } - - var val = xml.Attribute(attributeName)?.Value; - Attempt result = val.TryConvertTo(); + Attempt result = xmlAttribute.Value.TryConvertTo(); if (result.Success) { return result.Result; @@ -142,13 +137,13 @@ public static class XmlExtensions return default; } - if (xml.Attributes[attributeName] == null) + XmlAttribute? xmlAttribute = xml.Attributes[attributeName]; + if (xmlAttribute == null) { return default; } - var val = xml.Attributes[attributeName]?.Value; - Attempt result = val.TryConvertTo(); + Attempt result = xmlAttribute.Value.TryConvertTo(); if (result.Success) { return result.Result; diff --git a/src/Umbraco.Core/Handlers/WarnDocumentTypeElementSwitchNotificationHandler.cs b/src/Umbraco.Core/Handlers/WarnDocumentTypeElementSwitchNotificationHandler.cs index d7de162315..86ad4ad95c 100644 --- a/src/Umbraco.Core/Handlers/WarnDocumentTypeElementSwitchNotificationHandler.cs +++ b/src/Umbraco.Core/Handlers/WarnDocumentTypeElementSwitchNotificationHandler.cs @@ -56,12 +56,11 @@ public class WarnDocumentTypeElementSwitchNotificationHandler : foreach (IContentType savedDocumentType in notification.SavedEntities) { - if (stateInformation.ContainsKey(savedDocumentType.Key) is false) + if (stateInformation.TryGetValue(savedDocumentType.Key, out DocumentTypeElementSwitchInformation? state) is false) { continue; } - DocumentTypeElementSwitchInformation state = stateInformation[savedDocumentType.Key]; if (state.WasElement == savedDocumentType.IsElement) { // no change diff --git a/src/Umbraco.Core/HealthChecks/Checks/Services/SmtpCheck.cs b/src/Umbraco.Core/HealthChecks/Checks/Services/SmtpCheck.cs index 6119f4c715..7c013cb958 100644 --- a/src/Umbraco.Core/HealthChecks/Checks/Services/SmtpCheck.cs +++ b/src/Umbraco.Core/HealthChecks/Checks/Services/SmtpCheck.cs @@ -55,7 +55,8 @@ public class SmtpCheck : HealthCheck using (var writer = new StreamWriter(stream)) using (var reader = new StreamReader(stream)) { - writer.WriteLine("EHLO " + host); + writer.Write("EHLO "); + writer.WriteLine(host); writer.Flush(); reader.ReadLine(); return true; diff --git a/src/Umbraco.Core/Models/EntityContainer.cs b/src/Umbraco.Core/Models/EntityContainer.cs index c38c19159f..6033c6dfa9 100644 --- a/src/Umbraco.Core/Models/EntityContainer.cs +++ b/src/Umbraco.Core/Models/EntityContainer.cs @@ -66,12 +66,12 @@ public sealed class EntityContainer : TreeEntityBase, IUmbracoEntity /// The object type of containers containing objects of the contained object type. public static Guid GetContainerObjectType(Guid containedObjectType) { - if (ObjectTypeMap.ContainsKey(containedObjectType) == false) + if (ObjectTypeMap.TryGetValue(containedObjectType, out Guid containerObjectType) == false) { throw new ArgumentException("Not a contained object type.", nameof(containedObjectType)); } - return ObjectTypeMap[containedObjectType]; + return containerObjectType; } /// diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index b3c115c41b..f001797a4e 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; @@ -25,7 +26,7 @@ public static class UserExtensions { if (user.Avatar.IsNullOrWhiteSpace() || user.Avatar == "none") { - return new string[0]; + return []; } // use the custom avatar @@ -303,7 +304,7 @@ public static class UserExtensions usn.Add(sn); } - foreach (var sn in usn) + foreach (var sn in CollectionsMarshal.AsSpan(usn)) { var snp = paths[sn]; // has to be here now lsn.RemoveAll(x => diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index d8848c5a0d..7e63d6a0fe 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -422,10 +422,10 @@ public class PackagesRepository : ICreatedPackagesRepository } else { - if (processed.ContainsKey(dictionaryItem.ParentId.Value)) + if (processed.TryGetValue(dictionaryItem.ParentId.Value, out XElement? processedParent)) { // we've processed this parent element already so we can just append this xml child to it - AppendDictionaryElement(processed[dictionaryItem.ParentId.Value], items, processed, key, serializedDictionaryValue); + AppendDictionaryElement(processedParent, items, processed, key, serializedDictionaryValue); } else if (items.ContainsKey(dictionaryItem.ParentId.Value)) { diff --git a/src/Umbraco.Core/PublishedCache/PublishedElement.cs b/src/Umbraco.Core/PublishedCache/PublishedElement.cs index 92d8646539..ab62c3777b 100644 --- a/src/Umbraco.Core/PublishedCache/PublishedElement.cs +++ b/src/Umbraco.Core/PublishedCache/PublishedElement.cs @@ -43,7 +43,7 @@ public class PublishedElement : IPublishedElement return (IPublishedProperty)new PublishedElementPropertyBase(propertyType, this, previewing, referenceCacheLevel,cacheManager, value); }) .ToArray() - ?? new IPublishedProperty[0]; + ?? []; } // initializes a new instance of the PublishedElement class diff --git a/src/Umbraco.Core/Routing/SiteDomainMapper.cs b/src/Umbraco.Core/Routing/SiteDomainMapper.cs index b8ae10c3aa..247319cd5a 100644 --- a/src/Umbraco.Core/Routing/SiteDomainMapper.cs +++ b/src/Umbraco.Core/Routing/SiteDomainMapper.cs @@ -149,9 +149,9 @@ namespace Umbraco.Cms.Core.Routing Sites = null; } - if (Bindings != null && Bindings.ContainsKey(key)) + if (Bindings != null && Bindings.TryGetValue(key, out List? binding)) { - foreach (var b in Bindings[key]) + foreach (var b in binding) { Bindings[b].Remove(key); if (Bindings[b].Count == 0) @@ -292,10 +292,10 @@ namespace Umbraco.Cms.Core.Routing if (!currentSite.Equals(default(KeyValuePair))) { candidateSites = new[] { currentSite }; - if (Bindings != null && Bindings.ContainsKey(currentSite.Key)) + if (Bindings != null && Bindings.TryGetValue(currentSite.Key, out List? bindingForSite)) { IEnumerable> boundSites = - qualifiedSites.Where(site => Bindings[currentSite.Key].Contains(site.Key)); + qualifiedSites.Where(site => bindingForSite.Contains(site.Key)); candidateSites = candidateSites.Union(boundSites).ToArray(); // .ToArray ensures it is evaluated before the configuration lock is exited @@ -346,9 +346,9 @@ namespace Umbraco.Cms.Core.Routing } // cached? - if (_qualifiedSites != null && _qualifiedSites.ContainsKey(current.Scheme)) + if (_qualifiedSites != null && _qualifiedSites.TryGetValue(current.Scheme, out Dictionary? qualifiedSite)) { - return _qualifiedSites[current.Scheme]; + return qualifiedSite; } _qualifiedSites = _qualifiedSites ?? new Dictionary>(); diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 3cf1d86ce9..8ffb6e4094 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.DependencyInjection; @@ -558,7 +559,7 @@ public class ContentService : RepositoryService, IContentService scope.ReadLock(Constants.Locks.ContentTree); IEnumerable items = _documentRepository.GetMany(idsA); var index = items.ToDictionary(x => x.Id, x => x); - return idsA.Select(x => index.TryGetValue(x, out IContent? c) ? c : null).WhereNotNull(); + return idsA.Select(x => index.GetValueOrDefault(x)).WhereNotNull(); } } @@ -642,7 +643,7 @@ public class ContentService : RepositoryService, IContentService { var index = items.ToDictionary(x => x.Key, x => x); - return idsA.Select(x => index.TryGetValue(x, out IContent? c) ? c : null).WhereNotNull(); + return idsA.Select(x => index.GetValueOrDefault(x)).WhereNotNull(); } return Enumerable.Empty(); @@ -1999,7 +2000,7 @@ public class ContentService : RepositoryService, IContentService } // utility 'ShouldPublish' func used by SaveAndPublishBranch - private HashSet? PublishBranch_ShouldPublish(ref HashSet? cultures, string c, bool published, bool edited, bool isRoot, bool force) + private static HashSet? PublishBranch_ShouldPublish(ref HashSet? cultures, string c, bool published, bool edited, bool isRoot, bool force) { // if published, republish if (published) @@ -2841,7 +2842,7 @@ public class ContentService : RepositoryService, IContentService // - a copy is unpublished and therefore has no impact on tags in DB scope.Notifications.Publish( new ContentTreeChangeNotification(copy, TreeChangeTypes.RefreshBranch, eventMessages)); - foreach (Tuple x in copies) + foreach (Tuple x in CollectionsMarshal.AsSpan(copies)) { // FIXME: Pass parent key in constructor too when proper Copy method is implemented scope.Notifications.Publish(new ContentCopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, eventMessages)); @@ -3045,7 +3046,7 @@ public class ContentService : RepositoryService, IContentService return OperationResult.Succeed(eventMessages); } - private bool HasUnsavedChanges(IContent content) => content.HasIdentity is false || content.IsDirty(); + private static bool HasUnsavedChanges(IContent content) => content.HasIdentity is false || content.IsDirty(); public ContentDataIntegrityReport CheckDataIntegrity(ContentDataIntegrityReportOptions options) { @@ -3117,7 +3118,7 @@ public class ContentService : RepositoryService, IContentService private void Audit(AuditType type, int userId, int objectId, string? message = null, string? parameters = null) => _auditRepository.Save(new AuditItem(objectId, type, userId, UmbracoObjectTypes.Document.GetName(), message, parameters)); - private bool IsDefaultCulture(IReadOnlyCollection? langs, string culture) => + private static bool IsDefaultCulture(IReadOnlyCollection? langs, string culture) => langs?.Any(x => x.IsDefault && x.IsoCode.InvariantEquals(culture)) ?? false; private bool IsMandatoryCulture(IReadOnlyCollection langs, string culture) => diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index 74fe09f8c8..171a0dcf00 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.Runtime.InteropServices; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.DependencyInjection; @@ -128,7 +129,7 @@ public abstract class ContentTypeServiceBase : ContentTypeSe // eg maybe a property has been added, with an alias that's OK (no conflict with ancestors) // but that cannot be used (conflict with descendants) - IContentTypeComposition[] allContentTypes = Repository.GetMany(new int[0]).Cast().ToArray(); + IContentTypeComposition[] allContentTypes = Repository.GetMany(Array.Empty()).Cast().ToArray(); IEnumerable compositionAliases = compositionContentType.CompositionAliases(); IEnumerable compositions = allContentTypes.Where(x => compositionAliases.Any(y => x.Alias.Equals(y))); @@ -899,7 +900,7 @@ public abstract class ContentTypeServiceBase : ContentTypeSe //remove all composition that is not it's current alias var compositionAliases = clone.CompositionAliases().Except(new[] { alias }).ToList(); - foreach (var a in compositionAliases) + foreach (var a in CollectionsMarshal.AsSpan(compositionAliases)) { clone.RemoveContentType(a); } diff --git a/src/Umbraco.Core/Services/LocalizedTextService.cs b/src/Umbraco.Core/Services/LocalizedTextService.cs index 51012200bd..df4f7b3940 100644 --- a/src/Umbraco.Core/Services/LocalizedTextService.cs +++ b/src/Umbraco.Core/Services/LocalizedTextService.cs @@ -133,7 +133,7 @@ public class LocalizedTextService : ILocalizedTextService // TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode culture = ConvertToSupportedCultureWithRegionCode(culture); - if (DictionarySource.ContainsKey(culture) == false) + if (DictionarySource.TryGetValue(culture, out Lazy>>? valueForCulture) == false) { _logger.LogWarning( "The culture specified {Culture} was not found in any configured sources for this service", @@ -144,17 +144,14 @@ public class LocalizedTextService : ILocalizedTextService IDictionary result = new Dictionary(); // convert all areas + keys to a single key with a '/' - foreach (KeyValuePair> area in DictionarySource[culture].Value) + foreach (KeyValuePair> area in valueForCulture.Value) { foreach (KeyValuePair key in area.Value) { - var dictionaryKey = string.Format("{0}/{1}", area.Key, key.Key); + var dictionaryKey = $"{area.Key}/{key.Key}"; // i don't think it's possible to have duplicates because we're dealing with a dictionary in the first place, but we'll double check here just in case. - if (result.ContainsKey(dictionaryKey) == false) - { - result.Add(dictionaryKey, key.Value); - } + result.TryAdd(dictionaryKey, key.Value); } } @@ -219,7 +216,7 @@ public class LocalizedTextService : ILocalizedTextService // TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode culture = ConvertToSupportedCultureWithRegionCode(culture); - if (DictionarySource.ContainsKey(culture) == false) + if (DictionarySource.TryGetValue(culture, out Lazy>>? valueForCulture) == false) { _logger.LogWarning( "The culture specified {Culture} was not found in any configured sources for this service", @@ -227,7 +224,7 @@ public class LocalizedTextService : ILocalizedTextService return new Dictionary>(0); } - return DictionarySource[culture].Value; + return valueForCulture.Value; } public string Localize(string key, CultureInfo culture, IDictionary? tokens = null) @@ -288,10 +285,7 @@ public class LocalizedTextService : ILocalizedTextService { foreach (KeyValuePair alias in area.Value) { - if (!aliasValue.ContainsKey(alias.Key)) - { - aliasValue.Add(alias.Key, alias.Value); - } + aliasValue.TryAdd(alias.Key, alias.Value); } } @@ -343,7 +337,7 @@ public class LocalizedTextService : ILocalizedTextService return cultureDictionary; } - private IDictionary> GetAreaStoredTranslations( + private static IDictionary> GetAreaStoredTranslations( IDictionary> xmlSource, CultureInfo cult) { var overallResult = new Dictionary>(StringComparer.InvariantCulture); @@ -364,10 +358,7 @@ public class LocalizedTextService : ILocalizedTextService (string)key.Attribute("alias")!; // there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files - if (result.ContainsKey(dictionaryKey) == false) - { - result.Add(dictionaryKey, key.Value); - } + result.TryAdd(dictionaryKey, key.Value); } if (!overallResult.ContainsKey(areaAlias)) @@ -397,23 +388,17 @@ public class LocalizedTextService : ILocalizedTextService (string)key.Attribute("alias")!; // there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files - if (result.ContainsKey(dictionaryKey) == false) - { - result.Add(dictionaryKey, key.Value); - } + result.TryAdd(dictionaryKey, key.Value); } - if (!overallResult.ContainsKey(areaAlias)) - { - overallResult.Add(areaAlias, result); - } + overallResult.TryAdd(areaAlias, result); } } return overallResult; } - private Dictionary GetNoAreaStoredTranslations( + private static Dictionary GetNoAreaStoredTranslations( IDictionary> xmlSource, CultureInfo cult) { var result = new Dictionary(StringComparer.InvariantCulture); @@ -425,10 +410,7 @@ public class LocalizedTextService : ILocalizedTextService (string)key.Attribute("alias")!; // there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files - if (result.ContainsKey(dictionaryKey) == false) - { - result.Add(dictionaryKey, key.Value); - } + result.TryAdd(dictionaryKey, key.Value); } // Merge English Dictionary @@ -443,17 +425,14 @@ public class LocalizedTextService : ILocalizedTextService (string)key.Attribute("alias")!; // there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files - if (result.ContainsKey(dictionaryKey) == false) - { - result.Add(dictionaryKey, key.Value); - } + result.TryAdd(dictionaryKey, key.Value); } } return result; } - private Dictionary> GetAreaStoredTranslations( + private static Dictionary> GetAreaStoredTranslations( IDictionary>>> dictionarySource, CultureInfo cult) { @@ -481,7 +460,7 @@ public class LocalizedTextService : ILocalizedTextService private string GetFromDictionarySource(CultureInfo culture, string? area, string key, IDictionary? tokens) { - if (DictionarySource.ContainsKey(culture) == false) + if (DictionarySource.TryGetValue(culture, out Lazy>>? valueForCulture) == false) { _logger.LogWarning( "The culture specified {Culture} was not found in any configured sources for this service", @@ -496,7 +475,7 @@ public class LocalizedTextService : ILocalizedTextService } else { - if (DictionarySource[culture].Value.TryGetValue(area, out IDictionary? areaDictionary)) + if (valueForCulture.Value.TryGetValue(area, out IDictionary? areaDictionary)) { areaDictionary.TryGetValue(key, out found); } diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs b/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs index 8206bb024a..e1b9d7e1af 100644 --- a/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs +++ b/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs @@ -197,8 +197,8 @@ public class LocalizedTextServiceFileSources // This needs to be resolved before continuing so that the _twoLetterCultureConverter cache is initialized Dictionary> resolved = _xmlSources.Value; - return _twoLetterCultureConverter.ContainsKey(twoLetterCulture) - ? Attempt.Succeed(_twoLetterCultureConverter[twoLetterCulture]) + return _twoLetterCultureConverter.TryGetValue(twoLetterCulture, out CultureInfo? twoLetterCultureValue) + ? Attempt.Succeed(twoLetterCultureValue) : Attempt.Fail(); } diff --git a/src/Umbraco.Core/Services/MemberTypeService.cs b/src/Umbraco.Core/Services/MemberTypeService.cs index 72f4cde792..9700f51890 100644 --- a/src/Umbraco.Core/Services/MemberTypeService.cs +++ b/src/Umbraco.Core/Services/MemberTypeService.cs @@ -81,7 +81,7 @@ public class MemberTypeService : ContentTypeServiceBase e = _memberTypeRepository.GetMany(new int[0]).GetEnumerator()) + using (IEnumerator e = _memberTypeRepository.GetMany(Array.Empty()).GetEnumerator()) { if (e.MoveNext() == false) { diff --git a/src/Umbraco.Core/Services/PublicAccessService.cs b/src/Umbraco.Core/Services/PublicAccessService.cs index f44ba15022..24bdbc78c0 100644 --- a/src/Umbraco.Core/Services/PublicAccessService.cs +++ b/src/Umbraco.Core/Services/PublicAccessService.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.Runtime.InteropServices; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.DependencyInjection; @@ -99,9 +100,9 @@ internal class PublicAccessService : RepositoryService, IPublicAccessService { // This will retrieve from cache! var entries = _publicAccessRepository.GetMany().ToList(); - foreach (var id in ids) + foreach (var id in CollectionsMarshal.AsSpan(ids)) { - PublicAccessEntry? found = entries.FirstOrDefault(x => x.ProtectedNodeId == id); + PublicAccessEntry? found = entries.Find(x => x.ProtectedNodeId == id); if (found != null) { return found; diff --git a/src/Umbraco.Core/Services/TemplateContentParserService.cs b/src/Umbraco.Core/Services/TemplateContentParserService.cs index 19e6ce3d2c..21f70e10e4 100644 --- a/src/Umbraco.Core/Services/TemplateContentParserService.cs +++ b/src/Umbraco.Core/Services/TemplateContentParserService.cs @@ -14,12 +14,12 @@ public partial class TemplateContentParserService : ITemplateContentParserServic Match match = LayoutRegex().Match(viewContent); - if (match.Success == false || match.Groups.ContainsKey("layout") == false) + if (match.Success == false || match.Groups.TryGetValue("layout", out Group? layoutGroup) == false) { return null; } - var layout = match.Groups["layout"].Value; + var layout = layoutGroup.Value; return layout != "null" ? layout.Replace(".cshtml", string.Empty, StringComparison.OrdinalIgnoreCase) : null; diff --git a/src/Umbraco.Core/Services/UserGroupPermissionService.cs b/src/Umbraco.Core/Services/UserGroupPermissionService.cs index c8daa530fa..cbd06db181 100644 --- a/src/Umbraco.Core/Services/UserGroupPermissionService.cs +++ b/src/Umbraco.Core/Services/UserGroupPermissionService.cs @@ -9,6 +9,8 @@ namespace Umbraco.Cms.Core.Services; /// internal sealed class UserGroupPermissionService : IUserGroupPermissionService { + private static readonly Task _successTaskResult = Task.FromResult(UserGroupAuthorizationStatus.Success); + private static readonly Task _unauthorizedMissingUserGroupAccessTaskResult = Task.FromResult(UserGroupAuthorizationStatus.UnauthorizedMissingUserGroupAccess); private readonly IContentService _contentService; private readonly IMediaService _mediaService; private readonly IEntityService _entityService; @@ -27,24 +29,25 @@ internal sealed class UserGroupPermissionService : IUserGroupPermissionService } /// - public async Task AuthorizeAccessAsync(IUser user, IEnumerable userGroupKeys) + public Task AuthorizeAccessAsync(IUser user, IEnumerable userGroupKeys) { if (user.IsAdmin()) { - return UserGroupAuthorizationStatus.Success; + return _successTaskResult; } - var allowedUserGroupsKeys = user.Groups.Select(x => x.Key).ToArray(); - var missingAccess = userGroupKeys.Except(allowedUserGroupsKeys).ToArray(); + // LINQ.Except will make a HashSet of the argument passed in, so it is OK to use IEnumerable and avoid an allocation + var allowedUserGroupsKeys = user.Groups.Select(x => x.Key); + var missingAccess = userGroupKeys.Except(allowedUserGroupsKeys); - return missingAccess.Length == 0 - ? UserGroupAuthorizationStatus.Success - : UserGroupAuthorizationStatus.UnauthorizedMissingUserGroupAccess; + return missingAccess.Any() is false + ? _successTaskResult + : _unauthorizedMissingUserGroupAccessTaskResult; } /// - public async Task AuthorizeCreateAsync(IUser user, IUserGroup userGroup) - => ValidateAccess(user, userGroup); + public Task AuthorizeCreateAsync(IUser user, IUserGroup userGroup) + => Task.FromResult(ValidateAccess(user, userGroup)); /// @@ -100,7 +103,7 @@ internal sealed class UserGroupPermissionService : IUserGroupPermissionService /// /// to check for access. /// true if the user has access; otherwise, false. - private bool HasAccessToUsersSection(IUser user) + private static bool HasAccessToUsersSection(IUser user) => user.AllowedSections.Contains(Constants.Applications.Users); /// @@ -109,15 +112,15 @@ internal sealed class UserGroupPermissionService : IUserGroupPermissionService /// to check for access. /// The user group being created or updated. /// true if the user has access; otherwise, false. - private bool HasAccessToAllUserGroupSections(IUser user, IUserGroup userGroup) + private static bool HasAccessToAllUserGroupSections(IUser user, IUserGroup userGroup) { if (user.IsAdmin()) { return true; } - var sectionsMissingAccess = userGroup.AllowedSections.Except(user.AllowedSections).ToArray(); - return sectionsMissingAccess.Length == 0; + var sectionsMissingAccess = userGroup.AllowedSections.Except(user.AllowedSections); + return sectionsMissingAccess.Any() is false; } /// diff --git a/src/Umbraco.Core/Services/UserGroupService.cs b/src/Umbraco.Core/Services/UserGroupService.cs index 959d24a51c..1bf0a2870d 100644 --- a/src/Umbraco.Core/Services/UserGroupService.cs +++ b/src/Umbraco.Core/Services/UserGroupService.cs @@ -307,8 +307,7 @@ internal sealed class UserGroupService : RepositoryService, IUserGroupService } Guid[] checkedGroupMembersKeys = - EnsureNonAdminUserIsInSavedUserGroup(performingUser, groupMembersKeys ?? Enumerable.Empty()) - .ToArray(); + EnsureNonAdminUserIsInSavedUserGroup(performingUser, groupMembersKeys ?? []); IUser[] usersToAdd = (await _userService.GetAsync(checkedGroupMembersKeys)).ToArray(); // Since this is a brand new creation we don't have to be worried about what users were added and removed @@ -636,18 +635,16 @@ internal sealed class UserGroupService : RepositoryService, IUserGroupService /// /// This is to ensure that the user can access the group they themselves created at a later point and modify it. /// - private IEnumerable EnsureNonAdminUserIsInSavedUserGroup( + private static Guid[] EnsureNonAdminUserIsInSavedUserGroup( IUser performingUser, - IEnumerable groupMembersUserKeys) + Guid[] groupMembersUserKeys) { - var userKeys = groupMembersUserKeys.ToList(); - // If the performing user is an admin we don't care, they can access the group later regardless - if (performingUser.IsAdmin() is false && userKeys.Contains(performingUser.Key) is false) + if (performingUser.IsAdmin() is false && groupMembersUserKeys.Contains(performingUser.Key) is false) { - userKeys.Add(performingUser.Key); + return [..groupMembersUserKeys, performingUser.Key]; } - return userKeys; + return groupMembersUserKeys; } } diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 70a330b600..b46c1405e3 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Diagnostics; using System.Globalization; using Microsoft.Extensions.Options; @@ -47,11 +48,11 @@ namespace Umbraco.Cms.Core.Strings #region Filters // ok to be static here because it's not configurable in any way - private static readonly char[] InvalidFileNameChars = + private static readonly FrozenSet InvalidFileNameChars = Path.GetInvalidFileNameChars() .Union("!*'();:@&=+$,/?%#[]-~{}\"<>\\^`| ".ToCharArray()) .Distinct() - .ToArray(); + .ToFrozenSet(); public static bool IsValidFileNameChar(char c) { diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs index 7a4a968351..ce395436f2 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs @@ -48,12 +48,12 @@ public class DefaultShortStringHelperConfig culture = culture ?? string.Empty; - if (_configs.ContainsKey(culture) == false) + if (_configs.TryGetValue(culture, out Dictionary? configForCulture) == false) { - _configs[culture] = new Dictionary(); + configForCulture = _configs[culture] = new Dictionary(); } - _configs[culture][stringRole] = config; + configForCulture[stringRole] = config; return this; } @@ -134,37 +134,32 @@ public class DefaultShortStringHelperConfig culture = culture ?? string.Empty; stringType = stringType & CleanStringType.RoleMask; - Dictionary config; - if (_configs.ContainsKey(culture)) + if (_configs.TryGetValue(culture, out Dictionary? configForCulture)) { - config = _configs[culture]; - // have we got a config for _that_ role? - if (config.ContainsKey(stringType)) + if (configForCulture.TryGetValue(stringType, out Config? configForStringType)) { - return config[stringType]; + return configForStringType; } // have we got a generic config for _all_ roles? - if (config.ContainsKey(CleanStringType.RoleMask)) + if (configForCulture.TryGetValue(CleanStringType.RoleMask, out Config? configForRoleMask)) { - return config[CleanStringType.RoleMask]; + return configForRoleMask; } } - else if (_configs.ContainsKey(DefaultCulture)) + else if (_configs.TryGetValue(DefaultCulture, out Dictionary? configForDefaultCulture)) { - config = _configs[DefaultCulture]; - // have we got a config for _that_ role? - if (config.ContainsKey(stringType)) + if (configForDefaultCulture.TryGetValue(stringType, out Config? configForStringType)) { - return config[stringType]; + return configForStringType; } // have we got a generic config for _all_ roles? - if (config.ContainsKey(CleanStringType.RoleMask)) + if (configForDefaultCulture.TryGetValue(CleanStringType.RoleMask, out Config? configForRoleMask)) { - return config[CleanStringType.RoleMask]; + return configForRoleMask; } } diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index 1098072786..156d8e5b31 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -506,7 +506,7 @@ public class XmlHelper } xml = xml.Trim(); - return xml.StartsWith("<") && xml.EndsWith(">") && xml.Contains('/'); + return xml.StartsWith('<') && xml.EndsWith('>') && xml.Contains('/'); } /// diff --git a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs index 246df89e2a..ccdd259644 100644 --- a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs +++ b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs @@ -105,7 +105,7 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher { sb.Append("+__NodeTypeAlias:"); sb.Append(searchFrom); - sb.Append(" "); + sb.Append(' '); } break; @@ -261,16 +261,16 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher //additional fields normally sb.Append(f); - sb.Append(":"); - sb.Append("("); + sb.Append(':'); + sb.Append('('); foreach (var w in queryWordsReplaced) { sb.Append(w.ToLower()); sb.Append("* "); } - sb.Append(")"); - sb.Append(" "); + sb.Append(')'); + sb.Append(' '); } sb.Append(") "); @@ -284,7 +284,7 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher return true; } - private void AppendNodeNamePhraseWithBoost(StringBuilder sb, string query, IEnumerable allLangs) + private static void AppendNodeNamePhraseWithBoost(StringBuilder sb, string query, IEnumerable allLangs) { //node name exactly boost x 10 sb.Append("nodeName: ("); @@ -301,31 +301,31 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher } } - private void AppendNodeNameExactWithBoost(StringBuilder sb, string query, IEnumerable allLangs) + private static void AppendNodeNameExactWithBoost(StringBuilder sb, string query, IEnumerable allLangs) { //node name exactly boost x 10 sb.Append("nodeName:"); - sb.Append("\""); + sb.Append('"'); sb.Append(query.ToLower()); - sb.Append("\""); + sb.Append('"'); sb.Append("^10.0 "); //also search on all variant node names foreach (var lang in allLangs) { //node name exactly boost x 10 - sb.Append($"nodeName_{lang}:"); - sb.Append("\""); + sb.Append("nodeName_").Append(lang).Append(':'); + sb.Append('"'); sb.Append(query.ToLower()); - sb.Append("\""); + sb.Append('"'); sb.Append("^10.0 "); } } - private void AppendNodeNameWithWildcards(StringBuilder sb, string[] querywords, IEnumerable allLangs) + private static void AppendNodeNameWithWildcards(StringBuilder sb, string[] querywords, IEnumerable allLangs) { //node name normally with wildcards sb.Append("nodeName:"); - sb.Append("("); + sb.Append('('); foreach (var w in querywords) { sb.Append(w.ToLower()); @@ -338,7 +338,7 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher { //node name normally with wildcards sb.Append($"nodeName_{lang}:"); - sb.Append("("); + sb.Append('('); foreach (var w in querywords) { sb.Append(w.ToLower()); @@ -349,7 +349,7 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher } } - private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[]? startNodeIds, string? searchFrom, bool ignoreUserStartNodes, IEntityService entityService) + private static void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[]? startNodeIds, string? searchFrom, bool ignoreUserStartNodes, IEntityService entityService) { if (sb == null) { @@ -382,7 +382,7 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher // find... only what's underneath sb.Append("+__Path:"); AppendPath(sb, entityPath.Path, false); - sb.Append(" "); + sb.Append(' '); } else if (startNodeIds?.Length == 0) { @@ -405,7 +405,7 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher } else { - sb.Append(" "); + sb.Append(' '); } AppendPath(sb, ep.Path, true); @@ -415,13 +415,13 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher } } - private void AppendPath(StringBuilder sb, string path, bool includeThisNode) + private static void AppendPath(StringBuilder sb, string path, bool includeThisNode) { path = path.Replace("-", "\\-").Replace(",", "\\,"); if (includeThisNode) { sb.Append(path); - sb.Append(" "); + sb.Append(' '); } sb.Append(path); diff --git a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs index 99a31a0513..c78f4b0ec1 100644 --- a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs +++ b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs @@ -20,7 +20,7 @@ public static class ExamineExtensions { // TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll // also do this rudimentary check - if (!query.Contains(":")) + if (!query.Contains(':')) { return false; } diff --git a/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs index 84c1b3c4e4..563d98fa19 100644 --- a/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs +++ b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs @@ -13,7 +13,7 @@ public class LuceneRAMDirectoryFactory : DirectoryFactoryBase protected override Directory CreateDirectory(LuceneIndex luceneIndex, bool forceUnlock) => new RandomIdRAMDirectory(); - private class RandomIdRAMDirectory : RAMDirectory + private sealed class RandomIdRAMDirectory : RAMDirectory { private readonly string _lockId = Guid.NewGuid().ToString(); public override string GetLockID() => _lockId; diff --git a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs index bfec67f5d1..f1a26b7bd9 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs @@ -130,7 +130,7 @@ public abstract class UmbracoExamineIndex : LuceneIndex, IUmbracoIndex, IIndexDi /// /// Updates the index ValueSet with a special __Path and __Icon fields. /// - private void ApplySpecialIndexValueTransformations(IndexingItemEventArgs e) + private static void ApplySpecialIndexValueTransformations(IndexingItemEventArgs e) { var updatedValues = e.ValueSet.Values.ToDictionary(x => x.Key, x => (IEnumerable)x.Value); diff --git a/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs b/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs index 0ead853459..ee043ee095 100644 --- a/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs +++ b/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs @@ -1,6 +1,5 @@ using HtmlAgilityPack; using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.Blocks; using Umbraco.Cms.Core.Models.DeliveryApi; @@ -124,16 +123,16 @@ internal sealed class ApiRichTextElementParser : ApiRichTextParserBase, IApiRich return createElement(tag, attributes, childElements); } - private string TagName(HtmlNode htmlNode) => htmlNode.Name; + private static string TagName(HtmlNode htmlNode) => htmlNode.Name; private void ReplaceLocalLinks(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache, Dictionary attributes) { - if (attributes.ContainsKey("href") is false || attributes["href"] is not string href) + if (attributes.TryGetValue("href", out object? hrefAttribute) is false || hrefAttribute is not string href) { return; } - if (attributes.ContainsKey("type") is false || attributes["type"] is not string type) + if (attributes.TryGetValue("type", out object? typeAttribute) is false || typeAttribute is not string type) { type = "unknown"; } @@ -154,7 +153,7 @@ internal sealed class ApiRichTextElementParser : ApiRichTextParserBase, IApiRich private void ReplaceLocalImages(IPublishedMediaCache mediaCache, string tag, Dictionary attributes) { - if (tag is not "img" || attributes.ContainsKey("data-udi") is false || attributes["data-udi"] is not string dataUdi) + if (tag is not "img" || attributes.TryGetValue("data-udi", out object? dataUdiAttribute) is false || dataUdiAttribute is not string dataUdi) { return; } @@ -166,9 +165,9 @@ internal sealed class ApiRichTextElementParser : ApiRichTextParserBase, IApiRich }); } - private void CleanUpBlocks(string tag, Dictionary attributes) + private static void CleanUpBlocks(string tag, Dictionary attributes) { - if (tag.StartsWith("umb-rte-block") is false || attributes.ContainsKey(BlockContentKeyAttribute) is false || attributes[BlockContentKeyAttribute] is not string dataKey) + if (tag.StartsWith("umb-rte-block") is false || attributes.TryGetValue(BlockContentKeyAttribute, out object? blockContentKeyAttribute) is false || blockContentKeyAttribute is not string dataKey) { return; } diff --git a/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs b/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs index 520d5e8cb7..5ba7b37171 100644 --- a/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs +++ b/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs @@ -4,7 +4,6 @@ using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Templates; namespace Umbraco.Cms.Infrastructure.DeliveryApi; diff --git a/src/Umbraco.Infrastructure/Persistence/SqlTemplate.cs b/src/Umbraco.Infrastructure/Persistence/SqlTemplate.cs index ae2fa58f02..0a3ecc7464 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlTemplate.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlTemplate.cs @@ -39,7 +39,7 @@ public class SqlTemplate // see https://stackoverflow.com/questions/9256594 // => assume it's an anonymous type object containing named arguments // (of course this means we cannot use *real* objects here and need SqlNamed - bah) - if (args.Length == 1 && args[0].GetType().Name.Contains("<")) + if (args.Length == 1 && args[0].GetType().Name.Contains('<')) { return SqlNamed(args[0]); } @@ -54,7 +54,7 @@ public class SqlTemplate return new Sql(_sqlContext, true, _sql); } - var isBuilt = !args.Any(x => x is IEnumerable); + var isBuilt = !Array.Exists(args, x => x is IEnumerable); return new Sql(_sqlContext, isBuilt, _sql, args); } @@ -120,7 +120,7 @@ public class SqlTemplate new[] { default(T) }; // these are created in PocoToSqlExpressionVisitor - internal class TemplateArg + internal sealed class TemplateArg { public TemplateArg(string? name) => Name = name; diff --git a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs index f45243e47c..a00193850c 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs @@ -69,12 +69,9 @@ internal class CollectibleRuntimeViewCompiler : IViewCompiler foreach (CompiledViewDescriptor precompiledView in precompiledViews) { _logger.LogDebug("Initializing Razor view compiler with compiled view: '{ViewName}'", precompiledView.RelativePath); - if (!_precompiledViews.ContainsKey(precompiledView.RelativePath)) - { - // View ordering has precedence semantics, a view with a higher precedence was - // already added to the list. - _precompiledViews.Add(precompiledView.RelativePath, precompiledView); - } + // View ordering has precedence semantics, a view with a higher precedence was + // already added to the list. + _precompiledViews.TryAdd(precompiledView.RelativePath, precompiledView); } if (_precompiledViews.Count == 0) diff --git a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs index 0d5298e243..c25a485b55 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs @@ -100,12 +100,11 @@ internal class ModelsBuilderNotificationHandler : { IDictionary serverVars = notification.ServerVariables; - if (!serverVars.ContainsKey("umbracoUrls")) + if (!serverVars.TryGetValue("umbracoUrls", out object? umbracoUrlsObject)) { throw new ArgumentException("Missing umbracoUrls."); } - var umbracoUrlsObject = serverVars["umbracoUrls"]; if (umbracoUrlsObject == null) { throw new ArgumentException("Null umbracoUrls"); @@ -116,12 +115,12 @@ internal class ModelsBuilderNotificationHandler : throw new ArgumentException("Invalid umbracoUrls"); } - if (!serverVars.ContainsKey("umbracoPlugins")) + if (!serverVars.TryGetValue("umbracoPlugins", out object? umbracoPluginsObject)) { throw new ArgumentException("Missing umbracoPlugins."); } - if (!(serverVars["umbracoPlugins"] is Dictionary umbracoPlugins)) + if (!(umbracoPluginsObject is Dictionary umbracoPlugins)) { throw new ArgumentException("Invalid umbracoPlugins"); } diff --git a/src/Umbraco.Web.Common/Mvc/HtmlStringUtilities.cs b/src/Umbraco.Web.Common/Mvc/HtmlStringUtilities.cs index b568d457f4..a2a9ef5142 100644 --- a/src/Umbraco.Web.Common/Mvc/HtmlStringUtilities.cs +++ b/src/Umbraco.Web.Common/Mvc/HtmlStringUtilities.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Runtime.InteropServices; using System.Text; using HtmlAgilityPack; using Microsoft.AspNetCore.Html; @@ -56,7 +57,7 @@ public sealed class HtmlStringUtilities // we have to reverse the list in order to not override the changes to the nodes later targets.Reverse(); - foreach (HtmlNode target in targets) + foreach (HtmlNode target in CollectionsMarshal.AsSpan(targets)) { HtmlNode content = doc.CreateTextNode(target.InnerHtml); target.ParentNode.ReplaceChild(content, target); @@ -104,199 +105,198 @@ public sealed class HtmlStringUtilities { const string hellip = "…"; - using (var outputms = new MemoryStream()) + using var outputms = new MemoryStream(); + var lengthReached = false; + + using var outputtw = new StreamWriter(outputms); + using (var ms = new MemoryStream()) { - var lengthReached = false; - - using (var outputtw = new StreamWriter(outputms)) + using (var tw = new StreamWriter(ms)) { - using (var ms = new MemoryStream()) + tw.Write(html); + tw.Flush(); + ms.Position = 0; + var tagStack = new Stack(); + + using (TextReader tr = new StreamReader(ms)) { - using (var tw = new StreamWriter(ms)) + bool isInsideElement = false, + insideTagSpaceEncountered = false, + isTagClose = false; + + int ic, + + // currentLength = 0, + currentTextLength = 0; + + string currentTag = string.Empty, + tagContents = string.Empty; + + while ((ic = tr.Read()) != -1) { - tw.Write(html); - tw.Flush(); - ms.Position = 0; - var tagStack = new Stack(); + var write = true; - using (TextReader tr = new StreamReader(ms)) + switch ((char)ic) { - bool isInsideElement = false, - insideTagSpaceEncountered = false, - isTagClose = false; - - int ic, - - // currentLength = 0, - currentTextLength = 0; - - string currentTag = string.Empty, - tagContents = string.Empty; - - while ((ic = tr.Read()) != -1) - { - var write = true; - - switch ((char)ic) - { - case '<': - if (!lengthReached) - { - isInsideElement = true; - } - - insideTagSpaceEncountered = false; - currentTag = string.Empty; - tagContents = string.Empty; - isTagClose = false; - if (tr.Peek() == '/') - { - isTagClose = true; - } - - break; - - case '>': - isInsideElement = false; - - if (isTagClose && tagStack.Count > 0) - { - var thisTag = tagStack.Pop(); - outputtw.Write(""); - if (treatTagsAsContent) - { - currentTextLength++; - } - } - - if (!isTagClose && currentTag.Length > 0) - { - if (!lengthReached) - { - tagStack.Push(currentTag); - outputtw.Write("<" + currentTag); - if (treatTagsAsContent) - { - currentTextLength++; - } - - if (!string.IsNullOrEmpty(tagContents)) - { - if (tagContents.EndsWith("/")) - { - // No end tag e.g.
. - tagStack.Pop(); - } - - outputtw.Write(tagContents); - insideTagSpaceEncountered = false; - } - - outputtw.Write(">"); - } - } - - // Continue to next iteration of the text reader. - continue; - - default: - if (isInsideElement) - { - if (ic == ' ') - { - if (!insideTagSpaceEncountered) - { - insideTagSpaceEncountered = true; - } - } - - if (!insideTagSpaceEncountered) - { - currentTag += (char)ic; - } - } - - break; - } - - if (isInsideElement || insideTagSpaceEncountered) - { - write = false; - if (insideTagSpaceEncountered) - { - tagContents += (char)ic; - } - } - - if (!isInsideElement || treatTagsAsContent) - { - currentTextLength++; - } - - if (currentTextLength <= length || (lengthReached && isInsideElement)) - { - if (write) - { - var charToWrite = (char)ic; - outputtw.Write(charToWrite); - - // currentLength++; - } - } - + case '<': if (!lengthReached) { - if (currentTextLength == length) - { - // if the last character added was the first of a two character unicode pair, add the second character - if (char.IsHighSurrogate((char)ic)) - { - var lowSurrogate = tr.Read(); - outputtw.Write((char)lowSurrogate); - } - } + isInsideElement = true; + } - // only add elipsis if current length greater than original length - if (currentTextLength > length) - { - if (addElipsis) - { - outputtw.Write(hellip); - } + insideTagSpaceEncountered = false; + currentTag = string.Empty; + tagContents = string.Empty; + isTagClose = false; + if (tr.Peek() == '/') + { + isTagClose = true; + } - lengthReached = true; + break; + + case '>': + isInsideElement = false; + + if (isTagClose && tagStack.Count > 0) + { + var thisTag = tagStack.Pop(); + outputtw.Write("'); + if (treatTagsAsContent) + { + currentTextLength++; } } + + if (!isTagClose && currentTag.Length > 0) + { + if (!lengthReached) + { + tagStack.Push(currentTag); + outputtw.Write('<'); + outputtw.Write(currentTag); + if (treatTagsAsContent) + { + currentTextLength++; + } + + if (!string.IsNullOrEmpty(tagContents)) + { + if (tagContents.EndsWith('/')) + { + // No end tag e.g.
. + tagStack.Pop(); + } + + outputtw.Write(tagContents); + insideTagSpaceEncountered = false; + } + + outputtw.Write(">"); + } + } + + // Continue to next iteration of the text reader. + continue; + + default: + if (isInsideElement) + { + if (ic == ' ') + { + if (!insideTagSpaceEncountered) + { + insideTagSpaceEncountered = true; + } + } + + if (!insideTagSpaceEncountered) + { + currentTag += (char)ic; + } + } + + break; + } + + if (isInsideElement || insideTagSpaceEncountered) + { + write = false; + if (insideTagSpaceEncountered) + { + tagContents += (char)ic; + } + } + + if (!isInsideElement || treatTagsAsContent) + { + currentTextLength++; + } + + if (currentTextLength <= length || (lengthReached && isInsideElement)) + { + if (write) + { + var charToWrite = (char)ic; + outputtw.Write(charToWrite); + + // currentLength++; + } + } + + if (!lengthReached) + { + if (currentTextLength == length) + { + // if the last character added was the first of a two character unicode pair, add the second character + if (char.IsHighSurrogate((char)ic)) + { + var lowSurrogate = tr.Read(); + outputtw.Write((char)lowSurrogate); + } + } + + // only add elipsis if current length greater than original length + if (currentTextLength > length) + { + if (addElipsis) + { + outputtw.Write(hellip); + } + + lengthReached = true; } } } } - - outputtw.Flush(); - outputms.Position = 0; - using (TextReader outputtr = new StreamReader(outputms)) - { - string result; - - var firstTrim = outputtr.ReadToEnd().Replace(" ", " ").Trim(); - - // Check to see if there is an empty char between the hellip and the output string - // if there is, remove it - if (addElipsis && lengthReached && string.IsNullOrWhiteSpace(firstTrim) == false) - { - result = firstTrim[firstTrim.Length - hellip.Length - 1] == ' ' - ? firstTrim.Remove(firstTrim.Length - hellip.Length - 1, 1) - : firstTrim; - } - else - { - result = firstTrim; - } - - return new HtmlString(result); - } } } + + outputtw.Flush(); + outputms.Position = 0; + using (TextReader outputtr = new StreamReader(outputms)) + { + string result; + + var firstTrim = outputtr.ReadToEnd().Replace(" ", " ").Trim(); + + // Check to see if there is an empty char between the hellip and the output string + // if there is, remove it + if (addElipsis && lengthReached && string.IsNullOrWhiteSpace(firstTrim) == false) + { + result = firstTrim[firstTrim.Length - hellip.Length - 1] == ' ' + ? firstTrim.Remove(firstTrim.Length - hellip.Length - 1, 1) + : firstTrim; + } + else + { + result = firstTrim; + } + + return new HtmlString(result); + } } /// diff --git a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs index 6546203229..9cdff29795 100644 --- a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs @@ -254,14 +254,7 @@ public static class HtmlHelperRenderExtensions if (!metaData.AreaName.IsNullOrWhiteSpace()) { // set the area to the plugin area - if (routeVals.ContainsKey("area")) - { - routeVals["area"] = metaData.AreaName; - } - else - { - routeVals.Add("area", metaData.AreaName); - } + routeVals["area"] = metaData.AreaName; } return htmlHelper.ActionLink(actionName, metaData.ControllerName, routeVals); @@ -816,10 +809,7 @@ public static class HtmlHelperRenderExtensions object? additionalRouteVals = null) { // ensure that the multipart/form-data is added to the HTML attributes - if (htmlAttributes.ContainsKey("enctype") == false) - { - htmlAttributes.Add("enctype", "multipart/form-data"); - } + htmlAttributes.TryAdd("enctype", "multipart/form-data"); var tagBuilder = new TagBuilder("form"); tagBuilder.MergeAttributes(htmlAttributes); diff --git a/tests/Umbraco.Tests.Benchmarks/CollectionBenchmarks.cs b/tests/Umbraco.Tests.Benchmarks/CollectionBenchmarks.cs index 7944e7bbe3..f3e36a792f 100644 --- a/tests/Umbraco.Tests.Benchmarks/CollectionBenchmarks.cs +++ b/tests/Umbraco.Tests.Benchmarks/CollectionBenchmarks.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Linq; +using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; namespace Umbraco.Tests.Benchmarks @@ -7,20 +6,42 @@ namespace Umbraco.Tests.Benchmarks [MemoryDiagnoser] public class CollectionBenchmarks { - private static readonly IEnumerable _enumerable = Enumerable.Range(0, 1000); - private static readonly int[] _array = _enumerable.ToArray(); - private static readonly List _list = _enumerable.ToList(); + private const int Size = 1000; + private static readonly IEnumerable _IReadOnlyListEnumerable = Enumerable.Range(0, Size); + private static readonly int[] _array = _IReadOnlyListEnumerable.ToArray(); + private static readonly List _list = _IReadOnlyListEnumerable.ToList(); [Benchmark] - public int[] ToArray() + public int[] IReadOnlyListToArray() { - return _enumerable.ToArray(); + return _IReadOnlyListEnumerable.ToArray(); } [Benchmark] - public List ToList() + public List IReadOnlyListToList() { - return _enumerable.ToList(); + return _IReadOnlyListEnumerable.ToList(); + } + + // LINQ Where and many other enumerables does not know how many items there will be returned + private static IEnumerable YieldingEnumerable() + { + for (int i = 0; i < Size; i++) + { + yield return i; + } + } + + [Benchmark] + public int[] YieldingEnumerableToArray() + { + return YieldingEnumerable().ToArray(); + } + + [Benchmark] + public List YieldingEnumerableToList() + { + return YieldingEnumerable().ToList(); } [Benchmark] @@ -32,6 +53,14 @@ namespace Umbraco.Tests.Benchmarks } } + [Benchmark] + public void IterateAsSpanList() + { + foreach (int item in CollectionsMarshal.AsSpan(_list)) + { + } + } + [Benchmark] public void IterateList() { @@ -40,18 +69,21 @@ namespace Umbraco.Tests.Benchmarks } } - //BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.2006 (21H2) - //Intel Core i9-10885H CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores - //.NET SDK= 6.0.401 - // [Host] : .NET 6.0.9 (6.0.922.41905), X64 RyuJIT - // DefaultJob : .NET 6.0.9 (6.0.922.41905), X64 RyuJIT + //BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.4169/23H2/2023Update/SunValley3) + //Intel Core i7-10750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores + //.NET SDK 8.0.401 + // [Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 + // DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 // // - //| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated | - //|------------- |-----------:|---------:|---------:|-------:|-------:|----------:| - //| ToArray | 503.8 ns | 5.11 ns | 4.53 ns | 0.4807 | 0.0067 | 4,024 B | - //| ToList | 1,369.0 ns | 25.38 ns | 49.50 ns | 0.4845 | 0.0134 | 4,056 B | - //| IterateArray | 244.9 ns | 3.29 ns | 2.75 ns | - | - | - | - //| IterateList | 620.5 ns | 4.45 ns | 3.95 ns | - | - | - | + //| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | + //|-------------------------- |-----------:|---------:|---------:|-------:|-------:|----------:| + //| IReadOnlyListToArray | 227.2 ns | 4.35 ns | 4.46 ns | 0.6413 | - | 4024 B | + //| IReadOnlyListToList | 236.8 ns | 3.77 ns | 3.53 ns | 0.6464 | 0.0098 | 4056 B | + //| YieldingEnumerableToArray | 3,249.0 ns | 63.49 ns | 96.96 ns | 1.3580 | 0.0114 | 8528 B | + //| YieldingEnumerableToList | 2,791.3 ns | 54.36 ns | 76.21 ns | 1.3466 | 0.0229 | 8456 B | + //| IterateArray | 245.3 ns | 4.09 ns | 3.42 ns | - | - | - | + //| IterateAsSpanList | 249.9 ns | 3.10 ns | 2.90 ns | - | - | - | + //| IterateList | 543.2 ns | 10.86 ns | 17.84 ns | - | - | - | } }