From faf6b60234167f5443435243eb52187ab07d514c Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Fri, 16 Oct 2020 00:03:05 +1300 Subject: [PATCH] Reduce memory use by reducing array allocations --- src/Umbraco.Core/Composing/TypeFinder.cs | 2 +- src/Umbraco.Core/Constants-StringArrays.cs | 21 +++++++++++++++++++ .../Logging/Serilog/SerilogLogger.cs | 3 +-- src/Umbraco.Core/Models/CultureImpact.cs | 6 ++++++ src/Umbraco.Core/Models/Membership/User.cs | 8 +++---- .../Implement/TemplateRepository.cs | 15 +++++-------- .../Validators/DecimalValidator.cs | 2 +- .../Validators/EmailValidator.cs | 2 +- .../Validators/IntegerValidator.cs | 2 +- .../Validators/RegexValidator.cs | 2 +- .../Validators/RequiredValidator.cs | 6 +++--- .../Services/ContentTypeServiceExtensions.cs | 2 +- .../Services/Implement/ContentService.cs | 2 +- .../Services/UserServiceExtensions.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Core/UriExtensions.cs | 2 +- src/Umbraco.Core/Xml/XPath/MacroNavigator.cs | 2 +- .../Editors/BackOfficeServerVariables.cs | 2 +- src/Umbraco.Web/Mvc/RenderViewEngine.cs | 4 ++-- .../DateTimeConfigurationEditor.cs | 3 ++- 20 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 src/Umbraco.Core/Constants-StringArrays.cs diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 5ad1e4358047..5790e05945ff 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -195,7 +195,7 @@ private static IEnumerable GetFilteredAssemblies( if (excludeFromResults == null) excludeFromResults = new HashSet(); if (exclusionFilter == null) - exclusionFilter = new string[] { }; + exclusionFilter = Array.Empty(); return GetAllAssemblies() .Where(x => excludeFromResults.Contains(x) == false diff --git a/src/Umbraco.Core/Constants-StringArrays.cs b/src/Umbraco.Core/Constants-StringArrays.cs new file mode 100644 index 000000000000..ff01676a3ec1 --- /dev/null +++ b/src/Umbraco.Core/Constants-StringArrays.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core +{ + public static partial class Constants + { + + public static class StringArrays + { + + /// + /// String array containing "value" + /// + public static readonly string[] ValueLower = Constants.StringArrays.ValueLower; + } + } +} diff --git a/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs b/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs index a51628030e54..74a82c6106b2 100644 --- a/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs +++ b/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs @@ -184,8 +184,7 @@ private static void DumpThreadAborts(global::Serilog.ILogger logger, LogEventLev messageTemplate += "\r\nFailed to create a minidump"; //Log a new entry (as opposed to appending to same log entry) - logger.Write(level, ex, "Failed to create a minidump ({ExType}: {ExMessage})", - new object[]{ ex.GetType().FullName, ex.Message }); + logger.Write(level, ex, "Failed to create a minidump ({ExType}: {ExMessage})", ex.GetType().FullName, ex.Message); } } } diff --git a/src/Umbraco.Core/Models/CultureImpact.cs b/src/Umbraco.Core/Models/CultureImpact.cs index ca1898594167..39ec344ab5ca 100644 --- a/src/Umbraco.Core/Models/CultureImpact.cs +++ b/src/Umbraco.Core/Models/CultureImpact.cs @@ -64,6 +64,12 @@ private CultureImpact(string culture, bool isDefault = false) /// public static CultureImpact Invariant { get; } = new CultureImpact(null); + + /// + /// Gets the impact of the invariant culture as an array. + /// + public static CultureImpact[] InvariantArray { get; } = new[] { new CultureImpact(null) }; + /// /// Creates an impact instance representing the impact of a specific culture. /// diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 3d071b0a18f9..36f7ecb8252b 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -24,8 +24,8 @@ public User() _language = Current.Configs.Global().DefaultUILanguage; // TODO: inject _isApproved = true; _isLockedOut = false; - _startContentIds = new int[] { }; - _startMediaIds = new int[] { }; + _startContentIds = Array.Empty(); + _startMediaIds = Array.Empty(); //cannot be null _rawPasswordValue = ""; } @@ -52,8 +52,8 @@ public User(string name, string email, string username, string rawPasswordValue) _userGroups = new HashSet(); _isApproved = true; _isLockedOut = false; - _startContentIds = new int[] { }; - _startMediaIds = new int[] { }; + _startContentIds = Array.Empty(); + _startMediaIds = Array.Empty(); } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs index 217578091629..92f380e87779 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs @@ -570,6 +570,9 @@ private void AddChildren(ITemplate[] all, List descendants, string ma } } + private static readonly string[] _validTemplateDirs = new[] { SystemDirectories.MvcViews }; + private static readonly IEnumerable _validTemplateFileExtensions = new[] { "cshtml", "vbhtml" }; + /// /// Validates a /// @@ -582,17 +585,9 @@ public bool ValidateTemplate(ITemplate template) // are we using Path for something else?! var path = template.VirtualPath; - // get valid paths - var validDirs = new[] { SystemDirectories.MvcViews }; - - // get valid extensions - var validExts = new List(); - validExts.Add("cshtml"); - validExts.Add("vbhtml"); - // validate path and extension - var validFile = IOHelper.VerifyEditPath(path, validDirs); - var validExtension = IOHelper.VerifyFileExtension(path, validExts); + var validFile = IOHelper.VerifyEditPath(path, _validTemplateDirs); + var validExtension = IOHelper.VerifyFileExtension(path, _validTemplateFileExtensions); return validFile && validExtension; } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/DecimalValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/DecimalValidator.cs index 86db995566ff..7966ecf10fbd 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/DecimalValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/DecimalValidator.cs @@ -19,7 +19,7 @@ public IEnumerable Validate(object value, string valueType, ob var result = value.TryConvertTo(); if (result.Success == false) - yield return new ValidationResult("The value " + value + " is not a valid decimal", new[] { "value" }); + yield return new ValidationResult("The value " + value + " is not a valid decimal", Constants.StringArrays.ValueLower); } } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs index 4df11e4f600b..0e57188a47c2 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs @@ -21,7 +21,7 @@ public IEnumerable Validate(object value, string valueType, ob if (asString != string.Empty && emailVal.IsValid(asString) == false) { // TODO: localize these! - yield return new ValidationResult("Email is invalid", new[] { "value" }); + yield return new ValidationResult("Email is invalid", Constants.StringArrays.ValueLower); } } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/IntegerValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/IntegerValidator.cs index 335ddf772463..a11c4d545c37 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/IntegerValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/IntegerValidator.cs @@ -19,7 +19,7 @@ public IEnumerable Validate(object value, string valueType, ob var result = value.TryConvertTo(); if (result.Success == false) { - yield return new ValidationResult("The value " + value + " is not a valid integer", new[] { "value" }); + yield return new ValidationResult("The value " + value + " is not a valid integer", Constants.StringArrays.ValueLower); } } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs index 8e82d694a7ab..df206a9911b8 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs @@ -72,7 +72,7 @@ public IEnumerable ValidateFormat(object value, string valueTy if (string.IsNullOrWhiteSpace(format)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(format)); if (value == null || !new Regex(format).IsMatch(value.ToString())) { - yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), new[] { "value" }); + yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), Constants.StringArrays.ValueLower); } } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs index 1aa29870e5e0..e646cf5dbb08 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs @@ -35,7 +35,7 @@ public IEnumerable ValidateRequired(object value, string value { if (value == null) { - yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), new[] { "value" }); + yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), Constants.StringArrays.ValueLower); yield break; } @@ -43,7 +43,7 @@ public IEnumerable ValidateRequired(object value, string value { if (value.ToString().DetectIsEmptyJson()) { - yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" }); + yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), Constants.StringArrays.ValueLower); } yield break; @@ -51,7 +51,7 @@ public IEnumerable ValidateRequired(object value, string value if (value.ToString().IsNullOrWhiteSpace()) { - yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" }); + yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), Constants.StringArrays.ValueLower); } } } diff --git a/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs b/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs index 37f1e5127f01..5e2817f47ce4 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs @@ -131,7 +131,7 @@ internal static ContentTypeAvailableCompositionsResults GetAvailableCompositeCon private static IContentTypeComposition[] GetAncestors(IContentTypeComposition ctype, IContentTypeComposition[] allContentTypes) { - if (ctype == null) return new IContentTypeComposition[] {}; + if (ctype == null) return Array.Empty(); var ancestors = new List(); var parentId = ctype.ParentId; while (parentId > 0) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index b190e0c69a00..42e8cda8ae13 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -2647,7 +2647,7 @@ private PublishResult StrategyCanPublish(IScope scope, IContent content, bool ch var variesByCulture = content.ContentType.VariesByCulture(); var impactsToPublish = culturesPublishing == null - ? new[] { CultureImpact.Invariant } //if it's null it's invariant + ? CultureImpact.InvariantArray //if it's null it's invariant : culturesPublishing.Select(x => CultureImpact.Explicit(x, allLangs.Any(lang => lang.IsoCode.InvariantEquals(x) && lang.IsMandatory))).ToArray(); // publish the culture(s) diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs index e817d7925ab3..c002d4723df9 100644 --- a/src/Umbraco.Core/Services/UserServiceExtensions.cs +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -57,7 +57,7 @@ public static EntityPermissionSet GetPermissionsForPath(this IUserService servic /// public static void RemoveUserGroupPermissions(this IUserService userService, int groupId, params int[] entityIds) { - userService.ReplaceUserGroupPermissions(groupId, new char[] {}, entityIds); + userService.ReplaceUserGroupPermissions(groupId, Array.Empty(), entityIds); } /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index cbc1f5b85e4e..e62f71a8fdd5 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -130,6 +130,7 @@ + diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index 60d9cd6ead44..e2c5780dbd9e 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -136,6 +136,7 @@ internal static bool IsDefaultBackOfficeRequest(this Uri url, IGlobalSettings gl return false; } + private static string[] toInclude = new[] { ".aspx", ".ashx", ".asmx", ".axd", ".svc" }; /// /// This is a performance tweak to check if this not an ASP.Net server file /// .Net will pass these requests through to the module when in integrated mode. @@ -149,7 +150,6 @@ internal static bool IsClientSideRequest(this Uri url) { var ext = Path.GetExtension(url.LocalPath); if (ext.IsNullOrWhiteSpace()) return false; - var toInclude = new[] {".aspx", ".ashx", ".asmx", ".axd", ".svc"}; return toInclude.Any(ext.InvariantEquals) == false; } catch (ArgumentException) diff --git a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs b/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs index 8b4755107ad0..13bc0595f37e 100644 --- a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs +++ b/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs @@ -175,7 +175,7 @@ private class MacroRoot { public MacroRoot(IEnumerable parameters) { - Parameters = parameters == null ? new MacroParameter[] {} : parameters.ToArray(); + Parameters = parameters == null ? Array.Empty() : parameters.ToArray(); } public MacroParameter[] Parameters { get; private set; } diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 67e94c0d0611..a8a52431ae04 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -62,7 +62,7 @@ internal Dictionary BareMinimumServerVariables() {"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl", "iconApiBaseUrl"}}, {"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage", "canSendRequiredEmail", "usernameIsEmail"}}, {"application", new[] {"applicationPath", "cacheBuster"}}, - {"isDebuggingEnabled", new string[] { }}, + {"isDebuggingEnabled", Array.Empty()}, {"features", new [] {"disabledFeatures"}} }; //now do the filtering... diff --git a/src/Umbraco.Web/Mvc/RenderViewEngine.cs b/src/Umbraco.Web/Mvc/RenderViewEngine.cs index f8a636c87b78..3947ee5eef19 100644 --- a/src/Umbraco.Web/Mvc/RenderViewEngine.cs +++ b/src/Umbraco.Web/Mvc/RenderViewEngine.cs @@ -66,14 +66,14 @@ public override ViewEngineResult FindView(ControllerContext controllerContext, s { return ShouldFindView(controllerContext, false) ? base.FindView(controllerContext, viewName, masterName, useCache) - : new ViewEngineResult(new string[] { }); + : new ViewEngineResult(Array.Empty()); } public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { return ShouldFindView(controllerContext, true) ? base.FindPartialView(controllerContext, partialViewName, useCache) - : new ViewEngineResult(new string[] { }); + : new ViewEngineResult(Array.Empty()); } /// diff --git a/src/Umbraco.Web/PropertyEditors/DateTimeConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimeConfigurationEditor.cs index 91707d612290..18432a9fd3a6 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimeConfigurationEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DateTimeConfigurationEditor.cs @@ -9,13 +9,14 @@ namespace Umbraco.Web.PropertyEditors /// public class DateTimeConfigurationEditor : ConfigurationEditor { + private static readonly string[] _timeChars = new string[] { "H", "m", "s" }; public override IDictionary ToValueEditor(object configuration) { var d = base.ToValueEditor(configuration); var format = d["format"].ToString(); - d["pickTime"] = format.ContainsAny(new string[] { "H", "m", "s" }); + d["pickTime"] = format.ContainsAny(_timeChars); return d; }