From 66e702615870b4f78cb49a4f3d416b09715b6418 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 16 Apr 2025 18:28:04 -0700 Subject: [PATCH 1/5] GetExtensionRequirements optimizations --- .../Description/DotNet/DependencyHelper.cs | 46 ++++++++++++++----- .../ExtensionRequirementsJsonContext.cs | 12 +++++ .../DotNet/DependencyHelperTests.cs | 18 +++++++- 3 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs diff --git a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs index 3f9ed5e932..bf7a22c21d 100644 --- a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs +++ b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Runtime.InteropServices; +using System.Text.Json; using Microsoft.Azure.WebJobs.Script.ExtensionRequirements; using Microsoft.Extensions.DependencyModel; using Newtonsoft.Json.Linq; @@ -15,7 +17,15 @@ namespace Microsoft.Azure.WebJobs.Script.Description public static class DependencyHelper { private const string AssemblyNamePrefix = "assembly:"; - private static readonly Lazy> _ridGraph = new Lazy>(BuildRuntimesGraph); + private static readonly Assembly ThisAssembly = typeof(DependencyHelper).Assembly; + private static readonly string ThisAssemblyName = ThisAssembly.GetName().Name; + private static readonly Lazy> RidGraph = new Lazy>(BuildRuntimesGraph); + private static readonly JsonDocumentOptions JsonDocumentOptions = new() + { + CommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true + }; + private static string _runtimeIdentifier; private static Dictionary BuildRuntimesGraph() @@ -73,14 +83,22 @@ private static string GetRuntimesGraphJson() private static string GetResourceFileContents(string fileName) { - var assembly = typeof(DependencyHelper).Assembly; - using (Stream resource = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{fileName}")) + var assembly = ThisAssembly; + using (Stream resource = assembly.GetManifestResourceStream($"{ThisAssemblyName}.{fileName}")) using (var reader = new StreamReader(resource)) { return reader.ReadToEnd(); } } + private static Stream GetResourceStream(string fileName) + { + var manifestResourceName = $"{ThisAssemblyName}.{fileName}"; + var stream = ThisAssembly.GetManifestResourceStream(manifestResourceName); + + return stream ?? throw new InvalidOperationException($"The embedded resource '{manifestResourceName}' could not be found."); + } + internal static Dictionary GetRuntimeAssemblies(string assemblyManifestName) { string assembliesJson = GetResourceFileContents(assemblyManifestName); @@ -93,14 +111,20 @@ internal static Dictionary GetRuntimeAssemblies(s internal static ExtensionRequirementsInfo GetExtensionRequirements() { - string requirementsJson = GetResourceFileContents("extensionrequirements.json"); - JObject requirements = JObject.Parse(requirementsJson); + const string fileName = "extensionrequirements.json"; + + using var resourceStream = GetResourceStream(fileName); + using var doc = JsonDocument.Parse(resourceStream, JsonDocumentOptions); + + var jsonElementRoot = doc.RootElement; - var bundleRequirements = requirements["bundles"] - .ToObject(); + var bundleRequirements = jsonElementRoot.TryGetProperty("bundles", out var bundles) + ? bundles.Deserialize(ExtensionRequirementsJsonContext.Default.BundleRequirementArray) + : []; - var extensionRequirements = requirements["types"] - .ToObject(); + var extensionRequirements = jsonElementRoot.TryGetProperty("types", out var types) + ? types.Deserialize(ExtensionRequirementsJsonContext.Default.ExtensionStartupTypeRequirementArray) + : []; return new ExtensionRequirementsInfo(bundleRequirements, extensionRequirements); } @@ -115,7 +139,7 @@ internal static ExtensionRequirementsInfo GetExtensionRequirements() /// The runtime fallbacks for the provided identifier. public static RuntimeFallbacks GetDefaultRuntimeFallbacks(string rid) { - var ridGraph = _ridGraph.Value; + var ridGraph = RidGraph.Value; var runtimeFallbacks = new RuntimeFallbacks(rid); var fallbacks = new List(); @@ -184,7 +208,7 @@ public static List GetRuntimeFallbacks(string rid) /// bool if string in was in proper assembly representation format. public static bool IsAssemblyReferenceFormat(string assemblyFormatString) { - return assemblyFormatString != null && assemblyFormatString.StartsWith(AssemblyNamePrefix); + return assemblyFormatString != null && assemblyFormatString.StartsWith(AssemblyNamePrefix); } /// diff --git a/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs new file mode 100644 index 0000000000..bdb74d4519 --- /dev/null +++ b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Text.Json.Serialization; + +namespace Microsoft.Azure.WebJobs.Script.ExtensionRequirements +{ + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] + [JsonSerializable(typeof(BundleRequirement[]))] + [JsonSerializable(typeof(ExtensionStartupTypeRequirement[]))] + internal partial class ExtensionRequirementsJsonContext : JsonSerializerContext; +} diff --git a/test/WebJobs.Script.Tests/Description/DotNet/DependencyHelperTests.cs b/test/WebJobs.Script.Tests/Description/DotNet/DependencyHelperTests.cs index 113641b557..6f7236e47a 100644 --- a/test/WebJobs.Script.Tests/Description/DotNet/DependencyHelperTests.cs +++ b/test/WebJobs.Script.Tests/Description/DotNet/DependencyHelperTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Extensions.DependencyModel; using Xunit; @@ -13,6 +12,23 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Description { public class DependencyHelperTests { + [Fact] + public void GetExtensionRequirementsReturnsEmbededManifestContent() + { + var extensionRequirements = DependencyHelper.GetExtensionRequirements(); + + Assert.NotNull(extensionRequirements); + Assert.NotNull(extensionRequirements.BundleRequirementsByBundleId); + Assert.NotNull(extensionRequirements.ExtensionRequirementsByStartupType); + + // Ensure properties are populated for an item in the collection. + Assert.NotEmpty(extensionRequirements.ExtensionRequirementsByStartupType.First().Value.AssemblyName); + Assert.NotEmpty(extensionRequirements.ExtensionRequirementsByStartupType.First().Value.Name); + Assert.NotEmpty(extensionRequirements.ExtensionRequirementsByStartupType.First().Value.PackageName); + Assert.NotEmpty(extensionRequirements.ExtensionRequirementsByStartupType.First().Value.MinimumAssemblyVersion); + Assert.NotEmpty(extensionRequirements.ExtensionRequirementsByStartupType.First().Value.MinimumPackageVersion); + } + [Fact] public void GetDefaultRuntimeFallbacks_MatchesCurrentRuntimeFallbacks() { From e559dd1d4868ccd5800a4655b962a44c2f1074f2 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Tue, 22 Apr 2025 14:37:45 -0700 Subject: [PATCH 2/5] Add release notes --- release_notes.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/release_notes.md b/release_notes.md index b123c479ca..0161b9a8eb 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,6 +1,7 @@ -### Release notes - - +### Release notes + + +- Memory allocation optimizations in `DependencyHelper.GetExtensionRequirements` (#11022) - Memory allocation optimizations in `ScriptStartupTypeLocator.GetExtensionsStartupTypesAsync` (#11012) From e5f0958d9f65be2262436f394616f5cf4a7f18e5 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 5 May 2025 19:22:59 -0700 Subject: [PATCH 3/5] Optimize BuildRuntimesGraph method --- .../ScriptStartupTypeLocator.cs | 6 +- .../Description/DotNet/DependencyHelper.cs | 83 ++++++++----------- .../DotNet/RuntimeAssembliesConfig.cs | 15 ++++ .../DotNet/RuntimeAssembliesJsonContext.cs | 12 +++ .../Description/DotNet/RuntimeGraph.cs | 15 ++++ .../DotNet/RuntimeGraphJsonContext.cs | 12 +++ .../Description/DotNet/RuntimeInfo.cs | 13 +++ .../ExtensionRequirementsInfo.cs | 30 +++++-- .../ExtensionRequirementsJsonContext.cs | 6 +- 9 files changed, 133 insertions(+), 59 deletions(-) create mode 100644 src/WebJobs.Script/Description/DotNet/RuntimeAssembliesConfig.cs create mode 100644 src/WebJobs.Script/Description/DotNet/RuntimeAssembliesJsonContext.cs create mode 100644 src/WebJobs.Script/Description/DotNet/RuntimeGraph.cs create mode 100644 src/WebJobs.Script/Description/DotNet/RuntimeGraphJsonContext.cs create mode 100644 src/WebJobs.Script/Description/DotNet/RuntimeInfo.cs diff --git a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs index 61d774eb83..e45f3969dc 100644 --- a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs +++ b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs @@ -349,7 +349,11 @@ private bool IsDotnetIsolatedApp(IEnumerable functions, IEnvir private ExtensionRequirementsInfo GetExtensionRequirementsInfo() { ExtensionRequirementsInfo requirementsInfo = _extensionRequirementOptions.Value.Bundles != null || _extensionRequirementOptions.Value.Extensions != null - ? new ExtensionRequirementsInfo(_extensionRequirementOptions.Value.Bundles, _extensionRequirementOptions.Value.Extensions) + ? new ExtensionRequirementsInfo + { + Bundles = _extensionRequirementOptions.Value.Bundles.ToList(), + Types = _extensionRequirementOptions.Value.Extensions.ToList() + } : DependencyHelper.GetExtensionRequirements(); return requirementsInfo; } diff --git a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs index bf7a22c21d..4d4f2b089e 100644 --- a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs +++ b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs @@ -8,9 +8,9 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Text.Json; +using Microsoft.Azure.WebJobs.Script.Description.DotNet; using Microsoft.Azure.WebJobs.Script.ExtensionRequirements; using Microsoft.Extensions.DependencyModel; -using Newtonsoft.Json.Linq; namespace Microsoft.Azure.WebJobs.Script.Description { @@ -20,30 +20,27 @@ public static class DependencyHelper private static readonly Assembly ThisAssembly = typeof(DependencyHelper).Assembly; private static readonly string ThisAssemblyName = ThisAssembly.GetName().Name; private static readonly Lazy> RidGraph = new Lazy>(BuildRuntimesGraph); - private static readonly JsonDocumentOptions JsonDocumentOptions = new() - { - CommentHandling = JsonCommentHandling.Skip, - AllowTrailingCommas = true - }; private static string _runtimeIdentifier; private static Dictionary BuildRuntimesGraph() { - var ridGraph = new Dictionary(); - string runtimesJson = GetRuntimesGraphJson(); - var runtimes = (JObject)JObject.Parse(runtimesJson)["runtimes"]; + using var stream = GetResourceStream("runtimes.json"); + var parsed = JsonSerializer.Deserialize(stream, RuntimeGraphJsonContext.Default.RuntimeGraph); - foreach (var runtime in runtimes) + if (parsed is null) { - string[] imports = ((JObject)runtime.Value)["#import"] - ?.Values() - .ToArray(); + throw new InvalidOperationException("Failed to deserialize runtimes graph JSON."); + } + + var result = new Dictionary(parsed.Runtimes.Count, StringComparer.OrdinalIgnoreCase); - ridGraph.Add(runtime.Key, imports); + foreach (var (rid, info) in parsed.Runtimes) + { + result[rid] = info.Imports ?? []; } - return ridGraph; + return result; } private static string GetDefaultPlatformRid() @@ -76,57 +73,43 @@ private static string GetDefaultPlatformRid() return rid; } - private static string GetRuntimesGraphJson() - { - return GetResourceFileContents("runtimes.json"); - } - - private static string GetResourceFileContents(string fileName) - { - var assembly = ThisAssembly; - using (Stream resource = assembly.GetManifestResourceStream($"{ThisAssemblyName}.{fileName}")) - using (var reader = new StreamReader(resource)) - { - return reader.ReadToEnd(); - } - } - private static Stream GetResourceStream(string fileName) { - var manifestResourceName = $"{ThisAssemblyName}.{fileName}"; - var stream = ThisAssembly.GetManifestResourceStream(manifestResourceName); + var stream = ThisAssembly.GetManifestResourceStream($"{ThisAssemblyName}.{fileName}"); - return stream ?? throw new InvalidOperationException($"The embedded resource '{manifestResourceName}' could not be found."); + return stream ?? throw new InvalidOperationException($"The embedded resource '{ThisAssemblyName}.{fileName}' could not be found."); } internal static Dictionary GetRuntimeAssemblies(string assemblyManifestName) { - string assembliesJson = GetResourceFileContents(assemblyManifestName); - JObject assemblies = JObject.Parse(assembliesJson); + using var stream = GetResourceStream(assemblyManifestName); + var runtimeAssemblies = JsonSerializer.Deserialize(stream, RuntimeAssembliesJsonContext.Default.RuntimeAssembliesConfig); + + var assemblies = runtimeAssemblies?.RuntimeAssemblies ?? throw new InvalidOperationException($"Failed to retrieve runtime assemblies from the embedded resource '{assemblyManifestName}'."); + + var dictionary = new Dictionary(assemblies.Count, StringComparer.OrdinalIgnoreCase); + + foreach (var assembly in assemblies) + { + dictionary[assembly.Name] = assembly; + } - return assemblies["runtimeAssemblies"] - .ToObject() - .ToDictionary(a => a.Name, StringComparer.OrdinalIgnoreCase); + return dictionary; } internal static ExtensionRequirementsInfo GetExtensionRequirements() { const string fileName = "extensionrequirements.json"; - using var resourceStream = GetResourceStream(fileName); - using var doc = JsonDocument.Parse(resourceStream, JsonDocumentOptions); - - var jsonElementRoot = doc.RootElement; - - var bundleRequirements = jsonElementRoot.TryGetProperty("bundles", out var bundles) - ? bundles.Deserialize(ExtensionRequirementsJsonContext.Default.BundleRequirementArray) - : []; + using var stream = GetResourceStream(fileName); + var extensionRequirementsInfo = JsonSerializer.Deserialize(stream, ExtensionRequirementsJsonContext.Default.ExtensionRequirementsInfo); - var extensionRequirements = jsonElementRoot.TryGetProperty("types", out var types) - ? types.Deserialize(ExtensionRequirementsJsonContext.Default.ExtensionStartupTypeRequirementArray) - : []; + if (extensionRequirementsInfo is null) + { + throw new InvalidOperationException($"Failed to deserialize extension requirements from embedded resource '{fileName}'."); + } - return new ExtensionRequirementsInfo(bundleRequirements, extensionRequirements); + return extensionRequirementsInfo; } /// diff --git a/src/WebJobs.Script/Description/DotNet/RuntimeAssembliesConfig.cs b/src/WebJobs.Script/Description/DotNet/RuntimeAssembliesConfig.cs new file mode 100644 index 0000000000..8508bf3965 --- /dev/null +++ b/src/WebJobs.Script/Description/DotNet/RuntimeAssembliesConfig.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.Azure.WebJobs.Script.Description +{ + /// + /// Represents the configuration that lists runtime assemblies and their resolution policies. + /// + internal sealed class RuntimeAssembliesConfig + { + public List RuntimeAssemblies { get; set; } + } +} diff --git a/src/WebJobs.Script/Description/DotNet/RuntimeAssembliesJsonContext.cs b/src/WebJobs.Script/Description/DotNet/RuntimeAssembliesJsonContext.cs new file mode 100644 index 0000000000..a8a101aa69 --- /dev/null +++ b/src/WebJobs.Script/Description/DotNet/RuntimeAssembliesJsonContext.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.Azure.WebJobs.Script.Description.DotNet +{ + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true)] + [JsonSerializable(typeof(RuntimeAssembliesConfig))] + internal partial class RuntimeAssembliesJsonContext : JsonSerializerContext; +} diff --git a/src/WebJobs.Script/Description/DotNet/RuntimeGraph.cs b/src/WebJobs.Script/Description/DotNet/RuntimeGraph.cs new file mode 100644 index 0000000000..1915596c46 --- /dev/null +++ b/src/WebJobs.Script/Description/DotNet/RuntimeGraph.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.Azure.WebJobs.Script.Description.DotNet +{ + /// + /// Represents the runtime graph configuration defined in runtimes.json. + /// + internal sealed class RuntimeGraph + { + public Dictionary Runtimes { get; set; } = []; + } +} diff --git a/src/WebJobs.Script/Description/DotNet/RuntimeGraphJsonContext.cs b/src/WebJobs.Script/Description/DotNet/RuntimeGraphJsonContext.cs new file mode 100644 index 0000000000..9ec96afb2c --- /dev/null +++ b/src/WebJobs.Script/Description/DotNet/RuntimeGraphJsonContext.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.Azure.WebJobs.Script.Description.DotNet +{ + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true)] + [JsonSerializable(typeof(RuntimeGraph))] + internal partial class RuntimeGraphJsonContext : JsonSerializerContext; +} diff --git a/src/WebJobs.Script/Description/DotNet/RuntimeInfo.cs b/src/WebJobs.Script/Description/DotNet/RuntimeInfo.cs new file mode 100644 index 0000000000..7d3d0cb3e4 --- /dev/null +++ b/src/WebJobs.Script/Description/DotNet/RuntimeInfo.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Text.Json.Serialization; + +namespace Microsoft.Azure.WebJobs.Script.Description.DotNet +{ + internal sealed class RuntimeInfo + { + [JsonPropertyName("#import")] + public string[] Imports { get; set; } = []; + } +} diff --git a/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs index 51b9a7c1a6..7c32c6871c 100644 --- a/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs +++ b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs @@ -9,15 +9,35 @@ namespace Microsoft.Azure.WebJobs.Script.ExtensionRequirements { internal sealed class ExtensionRequirementsInfo { - public ExtensionRequirementsInfo(IEnumerable bundleRequirements, IEnumerable extensionRequirements) + private Dictionary _bundleRequirementsById; + private Dictionary _extensionRequirementsByStartupType; + private List _bundles = []; + private List _types = []; + + public List Bundles { - BundleRequirementsByBundleId = bundleRequirements?.ToDictionary(a => a.Id, StringComparer.OrdinalIgnoreCase); + get => _bundles; + set + { + _bundles = value ?? []; + _bundleRequirementsById = null; + } + } - ExtensionRequirementsByStartupType = extensionRequirements?.ToDictionary(a => a.Name, StringComparer.OrdinalIgnoreCase); + public List Types + { + get => _types; + set + { + _types = value ?? []; + _extensionRequirementsByStartupType = null; + } } - public Dictionary BundleRequirementsByBundleId { get; private set; } + public Dictionary BundleRequirementsByBundleId => + _bundleRequirementsById ??= Bundles.ToDictionary(b => b.Id, StringComparer.OrdinalIgnoreCase); - public Dictionary ExtensionRequirementsByStartupType { get; private set; } + public Dictionary ExtensionRequirementsByStartupType => + _extensionRequirementsByStartupType ??= Types.ToDictionary(t => t.Name, StringComparer.OrdinalIgnoreCase); } } diff --git a/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs index bdb74d4519..5d77a248c2 100644 --- a/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs +++ b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsJsonContext.cs @@ -1,12 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System.Text.Json; using System.Text.Json.Serialization; namespace Microsoft.Azure.WebJobs.Script.ExtensionRequirements { - [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] - [JsonSerializable(typeof(BundleRequirement[]))] - [JsonSerializable(typeof(ExtensionStartupTypeRequirement[]))] + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true)] + [JsonSerializable(typeof(ExtensionRequirementsInfo))] internal partial class ExtensionRequirementsJsonContext : JsonSerializerContext; } From 3724b6f35bfd60c63294b0008c9787e1d51af545 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 5 May 2025 20:27:28 -0700 Subject: [PATCH 4/5] null check. --- .../DependencyInjection/ScriptStartupTypeLocator.cs | 4 ++-- .../ExtensionRequirements/ExtensionRequirementsInfo.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs index e45f3969dc..5475124153 100644 --- a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs +++ b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs @@ -351,8 +351,8 @@ private ExtensionRequirementsInfo GetExtensionRequirementsInfo() ExtensionRequirementsInfo requirementsInfo = _extensionRequirementOptions.Value.Bundles != null || _extensionRequirementOptions.Value.Extensions != null ? new ExtensionRequirementsInfo { - Bundles = _extensionRequirementOptions.Value.Bundles.ToList(), - Types = _extensionRequirementOptions.Value.Extensions.ToList() + Bundles = _extensionRequirementOptions.Value.Bundles?.ToArray() ?? [], + Types = _extensionRequirementOptions.Value.Extensions?.ToArray() ?? [] } : DependencyHelper.GetExtensionRequirements(); return requirementsInfo; diff --git a/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs index 7c32c6871c..eed99dc0f8 100644 --- a/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs +++ b/src/WebJobs.Script/ExtensionRequirements/ExtensionRequirementsInfo.cs @@ -11,10 +11,10 @@ internal sealed class ExtensionRequirementsInfo { private Dictionary _bundleRequirementsById; private Dictionary _extensionRequirementsByStartupType; - private List _bundles = []; - private List _types = []; + private BundleRequirement[] _bundles = []; + private ExtensionStartupTypeRequirement[] _types = []; - public List Bundles + public BundleRequirement[] Bundles { get => _bundles; set @@ -24,7 +24,7 @@ public List Bundles } } - public List Types + public ExtensionStartupTypeRequirement[] Types { get => _types; set From c2d9c0d5eec348a23447f26ea24e9a3dfa17a62c Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Tue, 6 May 2025 06:59:12 -0700 Subject: [PATCH 5/5] Minor cleanup. --- .../Description/DotNet/DependencyHelper.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs index 4d4f2b089e..c6303984f6 100644 --- a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs +++ b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs @@ -25,22 +25,23 @@ public static class DependencyHelper private static Dictionary BuildRuntimesGraph() { - using var stream = GetResourceStream("runtimes.json"); - var parsed = JsonSerializer.Deserialize(stream, RuntimeGraphJsonContext.Default.RuntimeGraph); + using var stream = GetEmbeddedResourceStream("runtimes.json"); - if (parsed is null) + var runtimeGraph = JsonSerializer.Deserialize(stream, RuntimeGraphJsonContext.Default.RuntimeGraph); + + if (runtimeGraph is not { Runtimes.Count: > 0 }) { - throw new InvalidOperationException("Failed to deserialize runtimes graph JSON."); + throw new InvalidOperationException("Failed to deserialize runtimes graph JSON or runtimes section is empty."); } - var result = new Dictionary(parsed.Runtimes.Count, StringComparer.OrdinalIgnoreCase); + var ridGraph = new Dictionary(runtimeGraph.Runtimes.Count, StringComparer.OrdinalIgnoreCase); - foreach (var (rid, info) in parsed.Runtimes) + foreach (var (rid, info) in runtimeGraph.Runtimes) { - result[rid] = info.Imports ?? []; + ridGraph[rid] = info.Imports ?? []; } - return result; + return ridGraph; } private static string GetDefaultPlatformRid() @@ -73,7 +74,7 @@ private static string GetDefaultPlatformRid() return rid; } - private static Stream GetResourceStream(string fileName) + private static Stream GetEmbeddedResourceStream(string fileName) { var stream = ThisAssembly.GetManifestResourceStream($"{ThisAssemblyName}.{fileName}"); @@ -82,7 +83,7 @@ private static Stream GetResourceStream(string fileName) internal static Dictionary GetRuntimeAssemblies(string assemblyManifestName) { - using var stream = GetResourceStream(assemblyManifestName); + using var stream = GetEmbeddedResourceStream(assemblyManifestName); var runtimeAssemblies = JsonSerializer.Deserialize(stream, RuntimeAssembliesJsonContext.Default.RuntimeAssembliesConfig); var assemblies = runtimeAssemblies?.RuntimeAssemblies ?? throw new InvalidOperationException($"Failed to retrieve runtime assemblies from the embedded resource '{assemblyManifestName}'."); @@ -101,7 +102,7 @@ internal static ExtensionRequirementsInfo GetExtensionRequirements() { const string fileName = "extensionrequirements.json"; - using var stream = GetResourceStream(fileName); + using var stream = GetEmbeddedResourceStream(fileName); var extensionRequirementsInfo = JsonSerializer.Deserialize(stream, ExtensionRequirementsJsonContext.Default.ExtensionRequirementsInfo); if (extensionRequirementsInfo is null)