From 27835dccec9a13904d916993e6efd607ed3cdabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Wed, 29 Apr 2026 06:56:08 +0200 Subject: [PATCH 01/11] Experiment --- src/IntegrationTest.Shared/CommonEndpointConfig.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/IntegrationTest.Shared/CommonEndpointConfig.cs b/src/IntegrationTest.Shared/CommonEndpointConfig.cs index fbd2810..5373ad7 100644 --- a/src/IntegrationTest.Shared/CommonEndpointConfig.cs +++ b/src/IntegrationTest.Shared/CommonEndpointConfig.cs @@ -6,6 +6,13 @@ public static class CommonEndpointConfig { public static void Apply(EndpointConfiguration configuration) { + var hostId = Environment.GetEnvironmentVariable( + "AzureFunctionsWebHost__hostid"); + + var hostInstanceId = + Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"); + + Console.WriteLine($"Host ID: {hostId}, Instance Id: {hostInstanceId}"); configuration.UseTransport(new AzureServiceBusServerlessTransport(TopicTopology.Default)); configuration.EnableInstallers(); configuration.UsePersistence(); From 2b704be019a6c4dea73e3714a7e9ca7837c2f62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Wed, 29 Apr 2026 18:03:55 +0200 Subject: [PATCH 02/11] Detect host id from environment --- .../CommonEndpointConfig.cs | 7 - .../DeterministicGuid.cs | 164 ++++++++++++++++++ .../FunctionEndpointConfigurationBuilder.cs | 45 +++++ 3 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs diff --git a/src/IntegrationTest.Shared/CommonEndpointConfig.cs b/src/IntegrationTest.Shared/CommonEndpointConfig.cs index 5373ad7..fbd2810 100644 --- a/src/IntegrationTest.Shared/CommonEndpointConfig.cs +++ b/src/IntegrationTest.Shared/CommonEndpointConfig.cs @@ -6,13 +6,6 @@ public static class CommonEndpointConfig { public static void Apply(EndpointConfiguration configuration) { - var hostId = Environment.GetEnvironmentVariable( - "AzureFunctionsWebHost__hostid"); - - var hostInstanceId = - Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"); - - Console.WriteLine($"Host ID: {hostId}, Instance Id: {hostInstanceId}"); configuration.UseTransport(new AzureServiceBusServerlessTransport(TopicTopology.Default)); configuration.EnableInstallers(); configuration.UsePersistence(); diff --git a/src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs b/src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs new file mode 100644 index 0000000..113877f --- /dev/null +++ b/src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs @@ -0,0 +1,164 @@ +namespace NServiceBus; + +using System.Buffers; +using System.Buffers.Binary; +using System.IO.Hashing; +using System.Runtime.CompilerServices; +using System.Text; + +/// +/// Creates deterministic version 8 GUIDs from binary or textual input. +/// +/// +/// The same input always produces the same GUID. +/// +/// Use this type when a stable identifier must be derived from existing data, +/// such as host identifiers, partition identifiers, or deterministic fallback IDs. +/// +/// For generated time-ordered GUIDs optimized for insertion order or chronological +/// sorting, use instead. +/// +static class DeterministicGuid +{ + const int MaxStackLimit = 256; + const int LengthPrefixSize = sizeof(int); + + /// + /// Creates a deterministic version 8 GUID from the specified string. + /// + /// The string value used to derive the GUID. + /// A deterministic version 8 GUID derived from . + /// + /// Thrown when is null. + /// + public static Guid Create(string value) + { + ArgumentNullException.ThrowIfNull(value); + + return Create(value.AsSpan()); + } + + /// + /// Creates a deterministic version 8 GUID from the specified UTF-16 character data. + /// + /// + /// The character data used to derive the GUID. The value is encoded as UTF-8 before hashing. + /// + /// A deterministic version 8 GUID derived from . + public static Guid Create(ReadOnlySpan value) + { + var encoding = Encoding.UTF8; + var byteCount = encoding.GetByteCount(value); + + byte[]? rented = null; + + Span buffer = byteCount <= MaxStackLimit + ? stackalloc byte[MaxStackLimit] + : rented = ArrayPool.Shared.Rent(byteCount); + + try + { + var written = encoding.GetBytes(value, buffer); + return Create(buffer[..written]); + } + finally + { + if (rented is not null) + { + ArrayPool.Shared.Return(rented, clearArray: true); + } + } + } + + /// + /// Creates a deterministic version 8 GUID from the specified binary data. + /// + /// The binary data used to derive the GUID. + /// A deterministic version 8 GUID derived from . + /// + /// The input is hashed directly. No text encoding or framing is applied. + /// + public static Guid Create(ReadOnlySpan value) + { + Span hash = stackalloc byte[16]; + + _ = XxHash128.Hash(value, hash); + + // UUID version 8 + hash[6] = (byte)((hash[6] & 0x0F) | 0x80); + + // RFC 4122 / RFC 9562 variant + hash[8] = (byte)((hash[8] & 0x3F) | 0x80); + + return new Guid(hash, bigEndian: true); + } + + + /// + /// Creates a deterministic version 8 GUID from multiple string values. + /// + /// The string values used to derive the GUID. + /// A deterministic version 8 GUID derived from . + /// + /// Each value is UTF-8 encoded and length-prefixed before hashing. This avoids + /// ambiguity between different value sequences that would otherwise produce the + /// same concatenated text, such as ("ab", "c") and ("a", "bc"). + /// + /// + /// Thrown when any value in is null. + /// + [SkipLocalsInit] + public static Guid Create(params ReadOnlySpan values) + { + var encoding = Encoding.UTF8; + + var totalByteCount = 0; + + Span counts = values.Length <= 64 + ? stackalloc int[values.Length] + : new int[values.Length]; + + for (var i = 0; i < values.Length; i++) + { + var count = encoding.GetByteCount(values[i]); + counts[i] = count; + + totalByteCount = checked(totalByteCount + LengthPrefixSize + count); + } + + byte[]? rented = null; + + Span buffer = totalByteCount <= MaxStackLimit + ? stackalloc byte[MaxStackLimit] + : rented = ArrayPool.Shared.Rent(totalByteCount); + + try + { + var written = 0; + + for (var i = 0; i < values.Length; i++) + { + var count = counts[i]; + + BinaryPrimitives.WriteInt32LittleEndian( + buffer.Slice(written, LengthPrefixSize), + count); + + written += LengthPrefixSize; + + written += encoding.GetBytes( + values[i], + buffer[written..]); + } + + return Create(buffer[..written]); + } + finally + { + if (rented is not null) + { + ArrayPool.Shared.Return(rented, clearArray: true); + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs index f363f53..07f4cd6 100644 --- a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs +++ b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs @@ -1,6 +1,7 @@ namespace NServiceBus; using Configuration.AdvancedExtensibility; +using Microsoft.Extensions.Configuration; using Microsoft.Azure.Functions.Worker.Builder; using Microsoft.Extensions.DependencyInjection; @@ -31,6 +32,8 @@ public static EndpointConfiguration BuildReceiveEndpointConfiguration( var endpointConfiguration = new EndpointConfiguration(endpointName); endpointConfiguration.AssemblyScanner().Disable = true; + ConfigureDefaultHostIdentifier(endpointConfiguration, builder.Configuration); + var settings = endpointConfiguration.GetSettings(); var endpointServices = settings.GetOrCreateKeyedServiceCollection(builder.Services, endpointName); functionManifest.Configuration(endpointConfiguration, endpointServices, builder.Configuration, builder.Environment); @@ -68,6 +71,8 @@ public static EndpointConfiguration BuildSendOnlyEndpointConfiguration( var endpointConfiguration = new EndpointConfiguration(endpointName); endpointConfiguration.AssemblyScanner().Disable = true; + ConfigureDefaultHostIdentifier(endpointConfiguration, builder.Configuration); + var settings = endpointConfiguration.GetSettings(); var endpointServices = settings.GetOrCreateKeyedServiceCollection(builder.Services, endpointName); @@ -75,4 +80,44 @@ public static EndpointConfiguration BuildSendOnlyEndpointConfiguration( endpointConfiguration.SendOnly(); return endpointConfiguration; } + + static void ConfigureDefaultHostIdentifier(EndpointConfiguration endpointConfiguration, IConfiguration configuration) + { + var hostIdentifier = ResolveDefaultHostIdentifier(configuration); + + endpointConfiguration.UniquelyIdentifyRunningInstance() + .UsingCustomDisplayName(hostIdentifier) + .UsingCustomIdentifier(DeterministicGuid.Create(hostIdentifier)); + } + + static string ResolveDefaultHostIdentifier(IConfiguration configuration) + { + // this would be set if a user has explicitly set the Host ID + var functionsHostId = configuration[AzureFunctionsHostIdKey]; + if (!string.IsNullOrWhiteSpace(functionsHostId)) + { + return functionsHostId; + } + + // this would be set if running inside a function app + var websiteInstanceId = configuration[WebsiteInstanceIdKey]; + if (!string.IsNullOrWhiteSpace(websiteInstanceId)) + { + return websiteInstanceId; + } + + // this would be set if running inside a container app + var containerName = configuration[ContainerNameKey]; + if (!string.IsNullOrWhiteSpace(containerName)) + { + return containerName; + } + + // fallback to machine name for local development + return Environment.MachineName; + } + + const string AzureFunctionsHostIdKey = "AzureFunctionsWebHost:hostid"; + const string WebsiteInstanceIdKey = "WEBSITE_INSTANCE_ID"; + const string ContainerNameKey = "CONTAINER_NAME"; } \ No newline at end of file From dccb5bc1726c83ee2c943ecc6c38d592e89addea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Thu, 30 Apr 2026 07:44:49 +0200 Subject: [PATCH 03/11] Include runtime selection in example --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 17d0d98..89d49e7 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Create a local.settings.json file in the root of the project. The file should co { "IsEncrypted": false, "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", "AzureWebJobsStorage": "UseDevelopmentStorage=true", "AzureWebJobsServiceBus": "YourConnectionString", "BillingPrefix": "billing" From 91017e944333494751f45c922f264d6eea2c2d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Thu, 30 Apr 2026 14:23:00 +0200 Subject: [PATCH 04/11] Remove check for host id --- .../FunctionEndpointConfigurationBuilder.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs index 07f4cd6..388e8d8 100644 --- a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs +++ b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs @@ -92,13 +92,6 @@ static void ConfigureDefaultHostIdentifier(EndpointConfiguration endpointConfigu static string ResolveDefaultHostIdentifier(IConfiguration configuration) { - // this would be set if a user has explicitly set the Host ID - var functionsHostId = configuration[AzureFunctionsHostIdKey]; - if (!string.IsNullOrWhiteSpace(functionsHostId)) - { - return functionsHostId; - } - // this would be set if running inside a function app var websiteInstanceId = configuration[WebsiteInstanceIdKey]; if (!string.IsNullOrWhiteSpace(websiteInstanceId)) @@ -117,7 +110,6 @@ static string ResolveDefaultHostIdentifier(IConfiguration configuration) return Environment.MachineName; } - const string AzureFunctionsHostIdKey = "AzureFunctionsWebHost:hostid"; const string WebsiteInstanceIdKey = "WEBSITE_INSTANCE_ID"; const string ContainerNameKey = "CONTAINER_NAME"; } \ No newline at end of file From f58c240ef79fc63cd8ad86f123a0c94cc3e33997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Sat, 2 May 2026 07:37:08 +0200 Subject: [PATCH 05/11] Use new core guid implementation --- .../DeterministicGuid.cs | 164 ------------------ .../FunctionEndpointConfigurationBuilder.cs | 1 + 2 files changed, 1 insertion(+), 164 deletions(-) delete mode 100644 src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs diff --git a/src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs b/src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs deleted file mode 100644 index 113877f..0000000 --- a/src/NServiceBus.AzureFunctions.Common/DeterministicGuid.cs +++ /dev/null @@ -1,164 +0,0 @@ -namespace NServiceBus; - -using System.Buffers; -using System.Buffers.Binary; -using System.IO.Hashing; -using System.Runtime.CompilerServices; -using System.Text; - -/// -/// Creates deterministic version 8 GUIDs from binary or textual input. -/// -/// -/// The same input always produces the same GUID. -/// -/// Use this type when a stable identifier must be derived from existing data, -/// such as host identifiers, partition identifiers, or deterministic fallback IDs. -/// -/// For generated time-ordered GUIDs optimized for insertion order or chronological -/// sorting, use instead. -/// -static class DeterministicGuid -{ - const int MaxStackLimit = 256; - const int LengthPrefixSize = sizeof(int); - - /// - /// Creates a deterministic version 8 GUID from the specified string. - /// - /// The string value used to derive the GUID. - /// A deterministic version 8 GUID derived from . - /// - /// Thrown when is null. - /// - public static Guid Create(string value) - { - ArgumentNullException.ThrowIfNull(value); - - return Create(value.AsSpan()); - } - - /// - /// Creates a deterministic version 8 GUID from the specified UTF-16 character data. - /// - /// - /// The character data used to derive the GUID. The value is encoded as UTF-8 before hashing. - /// - /// A deterministic version 8 GUID derived from . - public static Guid Create(ReadOnlySpan value) - { - var encoding = Encoding.UTF8; - var byteCount = encoding.GetByteCount(value); - - byte[]? rented = null; - - Span buffer = byteCount <= MaxStackLimit - ? stackalloc byte[MaxStackLimit] - : rented = ArrayPool.Shared.Rent(byteCount); - - try - { - var written = encoding.GetBytes(value, buffer); - return Create(buffer[..written]); - } - finally - { - if (rented is not null) - { - ArrayPool.Shared.Return(rented, clearArray: true); - } - } - } - - /// - /// Creates a deterministic version 8 GUID from the specified binary data. - /// - /// The binary data used to derive the GUID. - /// A deterministic version 8 GUID derived from . - /// - /// The input is hashed directly. No text encoding or framing is applied. - /// - public static Guid Create(ReadOnlySpan value) - { - Span hash = stackalloc byte[16]; - - _ = XxHash128.Hash(value, hash); - - // UUID version 8 - hash[6] = (byte)((hash[6] & 0x0F) | 0x80); - - // RFC 4122 / RFC 9562 variant - hash[8] = (byte)((hash[8] & 0x3F) | 0x80); - - return new Guid(hash, bigEndian: true); - } - - - /// - /// Creates a deterministic version 8 GUID from multiple string values. - /// - /// The string values used to derive the GUID. - /// A deterministic version 8 GUID derived from . - /// - /// Each value is UTF-8 encoded and length-prefixed before hashing. This avoids - /// ambiguity between different value sequences that would otherwise produce the - /// same concatenated text, such as ("ab", "c") and ("a", "bc"). - /// - /// - /// Thrown when any value in is null. - /// - [SkipLocalsInit] - public static Guid Create(params ReadOnlySpan values) - { - var encoding = Encoding.UTF8; - - var totalByteCount = 0; - - Span counts = values.Length <= 64 - ? stackalloc int[values.Length] - : new int[values.Length]; - - for (var i = 0; i < values.Length; i++) - { - var count = encoding.GetByteCount(values[i]); - counts[i] = count; - - totalByteCount = checked(totalByteCount + LengthPrefixSize + count); - } - - byte[]? rented = null; - - Span buffer = totalByteCount <= MaxStackLimit - ? stackalloc byte[MaxStackLimit] - : rented = ArrayPool.Shared.Rent(totalByteCount); - - try - { - var written = 0; - - for (var i = 0; i < values.Length; i++) - { - var count = counts[i]; - - BinaryPrimitives.WriteInt32LittleEndian( - buffer.Slice(written, LengthPrefixSize), - count); - - written += LengthPrefixSize; - - written += encoding.GetBytes( - values[i], - buffer[written..]); - } - - return Create(buffer[..written]); - } - finally - { - if (rented is not null) - { - ArrayPool.Shared.Return(rented, clearArray: true); - } - } - } -} \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs index 388e8d8..0bcad07 100644 --- a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs +++ b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs @@ -4,6 +4,7 @@ namespace NServiceBus; using Microsoft.Extensions.Configuration; using Microsoft.Azure.Functions.Worker.Builder; using Microsoft.Extensions.DependencyInjection; +using Utils; /// /// Produces a configured for an Azure Functions-hosted endpoint. From eca845c6d497555cd49462334f0ae2ec25b2460a Mon Sep 17 00:00:00 2001 From: Daniel Marbach Date: Sat, 2 May 2026 08:31:40 +0200 Subject: [PATCH 06/11] Ship RuntimeHostConfigurationOption via props file so it flows to package consumers --- src/IntegrationTestApp/IntegrationTestApp.csproj | 4 ++++ .../NServiceBus.AzureFunctions.Common.props | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props diff --git a/src/IntegrationTestApp/IntegrationTestApp.csproj b/src/IntegrationTestApp/IntegrationTestApp.csproj index 918e245..3ac2268 100644 --- a/src/IntegrationTestApp/IntegrationTestApp.csproj +++ b/src/IntegrationTestApp/IntegrationTestApp.csproj @@ -24,6 +24,10 @@ + + + + diff --git a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props new file mode 100644 index 0000000..2f97c18 --- /dev/null +++ b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file From c115a5bfb12b196042209a7dc36ffcc6ddafa98c Mon Sep 17 00:00:00 2001 From: Brandon Ording Date: Mon, 4 May 2026 13:04:55 -0400 Subject: [PATCH 07/11] Tweaks --- .../NServiceBus.AzureFunctions.Common.csproj | 7 +++++++ .../NServiceBus.AzureFunctions.Common.props | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj index a2e0253..3654701 100644 --- a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj +++ b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj @@ -4,6 +4,7 @@ net10.0 true ..\NServiceBus.snk + $(TargetsForTfmSpecificContentInPackage);AddPropsFileToPackage @@ -20,4 +21,10 @@ + + + + + + diff --git a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props index 2f97c18..e374b6e 100644 --- a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props +++ b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props @@ -1,4 +1,4 @@ - + From b254154f2f9f501ffddd211a70abaee034df0492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Tue, 5 May 2026 08:41:55 +0200 Subject: [PATCH 08/11] Remove msbuild approach --- src/IntegrationTestApp/IntegrationTestApp.csproj | 4 ---- .../NServiceBus.AzureFunctions.Common.props | 7 ------- 2 files changed, 11 deletions(-) delete mode 100644 src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props diff --git a/src/IntegrationTestApp/IntegrationTestApp.csproj b/src/IntegrationTestApp/IntegrationTestApp.csproj index 3ac2268..918e245 100644 --- a/src/IntegrationTestApp/IntegrationTestApp.csproj +++ b/src/IntegrationTestApp/IntegrationTestApp.csproj @@ -24,10 +24,6 @@ - - - - diff --git a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props deleted file mode 100644 index e374b6e..0000000 --- a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From 0ac2f3f022538c412d6d3c4491c59f27c74f3852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Tue, 5 May 2026 08:44:00 +0200 Subject: [PATCH 09/11] More removal --- .../NServiceBus.AzureFunctions.Common.csproj | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj index 3654701..a2e0253 100644 --- a/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj +++ b/src/NServiceBus.AzureFunctions.Common/NServiceBus.AzureFunctions.Common.csproj @@ -4,7 +4,6 @@ net10.0 true ..\NServiceBus.snk - $(TargetsForTfmSpecificContentInPackage);AddPropsFileToPackage @@ -21,10 +20,4 @@ - - - - - - From 772ce0f0849daf66a95d213893db40f9d8ba880e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Tue, 5 May 2026 09:07:41 +0200 Subject: [PATCH 10/11] Refactor to a common method --- .../FunctionEndpointConfigurationBuilder.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs index 0bcad07..9e2f7c8 100644 --- a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs +++ b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs @@ -30,14 +30,10 @@ public static EndpointConfiguration BuildReceiveEndpointConfiguration( ArgumentNullException.ThrowIfNull(functionManifest); var endpointName = functionManifest.Name; - var endpointConfiguration = new EndpointConfiguration(endpointName); - endpointConfiguration.AssemblyScanner().Disable = true; - - ConfigureDefaultHostIdentifier(endpointConfiguration, builder.Configuration); - - var settings = endpointConfiguration.GetSettings(); - var endpointServices = settings.GetOrCreateKeyedServiceCollection(builder.Services, endpointName); - functionManifest.Configuration(endpointConfiguration, endpointServices, builder.Configuration, builder.Environment); + var endpointConfiguration = CreateDefaultEndpointConfiguration( + endpointName, + builder, + (configuration, endpointServices) => functionManifest.Configuration(configuration, endpointServices, builder.Configuration, builder.Environment)); if (endpointConfiguration.IsSendOnly) { @@ -69,26 +65,30 @@ public static EndpointConfiguration BuildSendOnlyEndpointConfiguration( ArgumentNullException.ThrowIfNull(endpointName); ArgumentNullException.ThrowIfNull(configure); - var endpointConfiguration = new EndpointConfiguration(endpointName); - endpointConfiguration.AssemblyScanner().Disable = true; - - ConfigureDefaultHostIdentifier(endpointConfiguration, builder.Configuration); - - var settings = endpointConfiguration.GetSettings(); - var endpointServices = settings.GetOrCreateKeyedServiceCollection(builder.Services, endpointName); + var endpointConfiguration = CreateDefaultEndpointConfiguration(endpointName, builder, configure); - configure(endpointConfiguration, endpointServices); endpointConfiguration.SendOnly(); + return endpointConfiguration; } - static void ConfigureDefaultHostIdentifier(EndpointConfiguration endpointConfiguration, IConfiguration configuration) + static EndpointConfiguration CreateDefaultEndpointConfiguration(string endpointName, FunctionsApplicationBuilder builder, Action userEndpointConfiguration) { - var hostIdentifier = ResolveDefaultHostIdentifier(configuration); + var endpointConfiguration = new EndpointConfiguration(endpointName); + endpointConfiguration.AssemblyScanner().Disable = true; + + var hostIdentifier = ResolveDefaultHostIdentifier(builder.Configuration); endpointConfiguration.UniquelyIdentifyRunningInstance() .UsingCustomDisplayName(hostIdentifier) .UsingCustomIdentifier(DeterministicGuid.Create(hostIdentifier)); + + var settings = endpointConfiguration.GetSettings(); + var endpointServices = settings.GetOrCreateKeyedServiceCollection(builder.Services, endpointName); + + userEndpointConfiguration(endpointConfiguration, endpointServices); + + return endpointConfiguration; } static string ResolveDefaultHostIdentifier(IConfiguration configuration) From fd5f8ae605b4230204339c1af36e0f32c78a1eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96hlund?= Date: Tue, 5 May 2026 09:12:20 +0200 Subject: [PATCH 11/11] Set UseV2DeterministicGuid app switch --- .../FunctionEndpointConfigurationBuilder.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs index 9e2f7c8..9685716 100644 --- a/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs +++ b/src/NServiceBus.AzureFunctions.Common/FunctionEndpointConfigurationBuilder.cs @@ -74,6 +74,11 @@ public static EndpointConfiguration BuildSendOnlyEndpointConfiguration( static EndpointConfiguration CreateDefaultEndpointConfiguration(string endpointName, FunctionsApplicationBuilder builder, Action userEndpointConfiguration) { + if (!AppContext.TryGetSwitch(UseV2DeterministicGuidAppSwitchKey, out _)) + { + AppContext.SetSwitch(UseV2DeterministicGuidAppSwitchKey, true); + } + var endpointConfiguration = new EndpointConfiguration(endpointName); endpointConfiguration.AssemblyScanner().Disable = true; @@ -113,4 +118,5 @@ static string ResolveDefaultHostIdentifier(IConfiguration configuration) const string WebsiteInstanceIdKey = "WEBSITE_INSTANCE_ID"; const string ContainerNameKey = "CONTAINER_NAME"; + const string UseV2DeterministicGuidAppSwitchKey = "NServiceBus.Core.Hosting.UseV2DeterministicGuid"; } \ No newline at end of file