.NET 9 Azure Functions V4独立模式下NServiceBus迁移配置问题求助
问题背景
将基于.NET 6的Azure Functions v3项目迁移至.NET 9和Azure Functions v4独立工作者模式时,NServiceBus与Azure Service Bus的集成配置出现兼容性问题,原Transport相关配置无法直接适配新版本API。
原配置代码
NServiceBus配置方法
public static IHostBuilder ConfigureNServiceBus(this IHostBuilder hostBuilder) { // Makes sure JObject is included in the NServiceBus scan JObject _ = new(); var auditQueue = EnvironmentVariableHelper.GetConfigValue(IoC.Configuration, "AuditQueue", "audit"); var errorQueue = EnvironmentVariableHelper.GetConfigValue(IoC.Configuration, "ErrorQueue", "error"); var azureTableStorageConnectionString = EnvironmentVariableHelper.GetStringConfigValue("BlobStorageConnection"); var endpointName = EnvironmentVariableHelper.GetStringConfigValue("ENDPOINT_NAME"); var tableStorageName = endpointName.Replace("-", string.Empty).Replace(".", string.Empty); hostBuilder.UseNServiceBus(configuration => { var endpointConfiguration = configuration.AdvancedConfiguration; endpointConfiguration.AuditProcessedMessagesTo(auditQueue); endpointConfiguration.SendFailedMessagesTo(errorQueue); endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>(); var transport = configuration.Transport; transport.TopicName = $"{endpointName}.events"; transport.SubscriptionRuleNamingConvention = eventType => eventType.Name.Replace("App.Contracts", string.Empty); var conventions = endpointConfiguration.Conventions(); conventions.DefiningCommandsAs(type => type.Namespace != null && type.Namespace.StartsWith("App.Contracts.Commands")); conventions.DefiningEventsAs(type => type.Namespace != null && type.Namespace.StartsWith("App.Contracts.Events")); var persistence = endpointConfiguration.UsePersistence<AzureTablePersistence>(); var tableServiceClient = new TableServiceClient(azureTableStorageConnectionString); persistence.UseTableServiceClient(tableServiceClient); persistence.DefaultTable(tableStorageName); endpointConfiguration.AuditSagaStateChanges(serviceControlQueue: auditQueue); ExcludeAssmbliesFromScan(endpointConfiguration); endpointConfiguration.EnableInstallers(); #if DEBUG transport.MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(30); #endif }); return hostBuilder; }
项目依赖配置
<ItemGroup> <FrameworkReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" /> <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.1" /> <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" /> <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" /> <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" /> <PackageReference Include="Google.Apis.Auth.AspNetCore3" Version="1.69.0" /> <PackageReference Include="Mapster" Version="7.4.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.3" /> <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.OpenApi" Version="1.5.1" /> <PackageReference Include="Microsoft.Graph" Version="5.74.0" /> <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.*" /> <PackageReference Include="Microsoft.SqlServer.TransactSql.ScriptDom" Version="170.28.0" /> <PackageReference Include="NServiceBus.Newtonsoft.Json" Version="4.1.0" /> <PackageReference Include="NServiceBus.Extensions.Hosting" Version="2.0.0" /> <PackageReference Include="NServiceBus.AzureFunctions.Worker.ServiceBus" Version="6.0.0" /> <PackageReference Include="NSwag.Generation.AspNetCore" Version="14.2.0" /> <PackageReference Include="TinyMapper" Version="3.0.3" /> </ItemGroup>
错误尝试与报错信息
尝试直接实例化AzureServiceBusTransport进行配置:
var transport = new AzureServiceBusTransport(azureTableStorageConnectionString, TopicTopology.Default); transport.TopicName($"{endpointName}.events"); transport.SubscriptionRuleNamingConvention = eventType => eventType.Name.Replace("App.Contracts", string.Empty); endpointConfiguration.UseTransport(transport);
出现以下报错:
'AzureServiceBusTransport' does not contain a definition for 'TopicName' and the best extension method overload 'AzureServiceBusTransportSettingsExtensions.TopicName(TransportExtensions, string)' requires a receiver of type 'NServiceBus.TransportExtensions<NServiceBus.AzureServiceBusTransport>'
'AzureServiceBusTransport.SubscriptionRuleNamingConvention' is obsolete: 'Setting the subscription rule name is accessible via the migration topology. Use
MigrationTopology.EventToMigrate<TEventType>(string? ruleNameOverride = null)instead. The member currently throws a NotImplementedException. Will be removed in version 6.0.0.'
修正后的配置代码
public static IHostBuilder ConfigureNServiceBus(this IHostBuilder hostBuilder) { // Makes sure JObject is included in the NServiceBus scan JObject _ = new(); var auditQueue = EnvironmentVariableHelper.GetConfigValue(IoC.Configuration, "AuditQueue", "audit"); var errorQueue = EnvironmentVariableHelper.GetConfigValue(IoC.Configuration, "ErrorQueue", "error"); // 修正:获取Azure Service Bus专用连接字符串(原代码误用了Blob存储连接字符串) var azureServiceBusConnectionString = EnvironmentVariableHelper.GetStringConfigValue("AzureServiceBusConnectionString"); var azureTableStorageConnectionString = EnvironmentVariableHelper.GetStringConfigValue("BlobStorageConnection"); var endpointName = EnvironmentVariableHelper.GetStringConfigValue("ENDPOINT_NAME"); var tableStorageName = endpointName.Replace("-", string.Empty).Replace(".", string.Empty); hostBuilder.UseNServiceBus(configuration => { var endpointConfiguration = configuration.AdvancedConfiguration; endpointConfiguration.AuditProcessedMessagesTo(auditQueue); endpointConfiguration.SendFailedMessagesTo(errorQueue); endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>(); // 修正:通过UseTransport获取TransportExtensions实例,正确配置传输 var transport = endpointConfiguration.UseTransport<AzureServiceBusTransport>(); transport.ConnectionString(azureServiceBusConnectionString); // 正确设置TopicName transport.TopicName($"{endpointName}.events"); // 替换废弃的SubscriptionRuleNamingConvention,使用当前API的SubscriptionNamingConvention transport.SubscriptionNamingConvention(type => type.Name.Replace("App.Contracts", string.Empty)); var conventions = endpointConfiguration.Conventions(); conventions.DefiningCommandsAs(type => type.Namespace != null && type.Namespace.StartsWith("App.Contracts.Commands")); conventions.DefiningEventsAs(type => type.Namespace != null && type.Namespace.StartsWith("App.Contracts.Events")); var persistence = endpointConfiguration.UsePersistence<AzureTablePersistence>(); var tableServiceClient = new TableServiceClient(azureTableStorageConnectionString); persistence.UseTableServiceClient(tableServiceClient); persistence.DefaultTable(tableStorageName); endpointConfiguration.AuditSagaStateChanges(serviceControlQueue: auditQueue); ExcludeAssmbliesFromScan(endpointConfiguration); endpointConfiguration.EnableInstallers(); #if DEBUG // 修正:通过TransportExtensions配置MaxAutoLockRenewalDuration transport.MaxAutoLockRenewalDuration(TimeSpan.FromMinutes(30)); #endif }); return hostBuilder; }
关键修改说明
- 传输连接字符串修正:原代码错误地将Blob存储连接字符串传给Azure Service Bus传输,需替换为Azure Service Bus的专用连接字符串。
- TopicName配置方式:新版本API中需通过
endpointConfiguration.UseTransport<AzureServiceBusTransport>()获取TransportExtensions<AzureServiceBusTransport>实例,再调用TopicName扩展方法设置,不能直接实例化AzureServiceBusTransport。 - 订阅规则命名替换:原
SubscriptionRuleNamingConvention已废弃,在默认拓扑下改用SubscriptionNamingConvention定义订阅规则名称。 - 调试模式配置修正:
MaxAutoLockRenewalDuration同样需通过TransportExtensions实例的扩展方法设置。
内容的提问来源于stack exchange,提问作者Felipe Siqueira Queiroz

