﻿# Azure Functions with Azure Service Bus (In Process)


> [!WARNING]
> **NServiceBus.AzureFunctions.InProcess.ServiceBus component has been sunset**
>
> Microsoft announced that .NET 8 will be [the last release supporting the in-process hosting model](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/net-on-azure-functions-august-2023-roadmap-update/ba-p/3910098).
> We will continue to provide support and address critical fixes during the sunset period, but no new features will be added.
> New projects should use the [isolated worker model](/nservicebus/hosting/azure-functions-service-bus/index.md) instead. Follow [these instructions](/nservicebus/upgrades/azure-functions-service-bus-in-process-isolated-worker.md) to migrate.
> For information on the date when this component will stop receiving any update check our [support policy page](/nservicebus/upgrades/all-versions.md#host-packages-nservicebus-azurefunctions-inprocess-servicebus).


Host NServiceBus endpoints with [Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/) and [Azure Service Bus](https://azure.microsoft.com/en-us/services/service-bus/) triggers.

## Basic usage

### Endpoint configuration

NServiceBus can be registered and configured on the host builder using the `UseNServiceBus` extension method in the startup class:

<!-- snippet: asb-function-default -->

```cs
[assembly: FunctionsStartup(typeof(Startup))]
[assembly: NServiceBusTriggerFunction("MyFunctionsEndpoint")]

class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.UseNServiceBus();
    }
}
```

<!-- endsnippet -->

Additional configuration settings are retrieved from environment variables. See the [configuration section](#configuration) for further details. All configuration settings can also be configured directly via code:

<!-- snippet: asb-function-hostbuilder -->

```cs
class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.UseNServiceBus(configuration =>
        {
            var transport = configuration.Transport;
            // Configure transport
        });
    }
}
```

<!-- endsnippet -->

Any services registered via the `IFunctionsHostBuilder` will be available to message handlers via dependency injection. The startup class must be declared via the `FunctionStartup` attribute: `[assembly: FunctionsStartup(typeof(Startup))]`.

### Azure Function queue trigger for NServiceBus

The Azure Function trigger for NServiceBus is auto-generated by specifying the logical endpoint name using a custom assembly attribute:

```csharp
[assembly: NServiceBusTriggerFunction("MyFunctionsEndpoint")]
```

The attribute will generate the trigger function required for NServiceBus:

> [!NOTE]
> An invalid endpoint name will generate an `NSBFUNC003` error with the message `Endpoint name is invalid and cannot be used to generate trigger function`.

#### Overriding the trigger function name

The trigger function name is auto-generated by default. To customize the function name, the `NServiceBusTriggerFunction` attribute can be provided with an additional parameter to set the function name:

<!-- snippet: asb-function-override-trigger-function-name -->

```txt
[assembly: NServiceBusTriggerFunctionAttribute(EndpointName: "MyFunctionsEndpoint", TriggerFunctionName: "MyTriggerFunction")]
```

<!-- endsnippet -->

> [!NOTE]
> An invalid trigger function name will generate an `NSBFUNC004` error with the message `Trigger function name is invalid and cannot be used to generate trigger function`.

#### Customizing triggers

The Azure Service Bus trigger can be declared manually instead of relying on the auto-generated trigger. See the article on [custom Azure Functions triggers](/nservicebus/hosting/azure-functions-service-bus/in-process/custom-triggers.md) for more information.

### Dispatching outside a message handler

Triggering a message using HTTP function:

<!-- snippet: asb-dispatching-outside-message-handler -->

```cs
public class HttpSender
{
    readonly IFunctionEndpoint functionEndpoint;

    public HttpSender(IFunctionEndpoint functionEndpoint)
    {
        this.functionEndpoint = functionEndpoint;
    }

    [FunctionName("HttpSender")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest request, ExecutionContext executionContext, ILogger logger)
    {
        logger.LogInformation("C# HTTP trigger function received a request.");

        var sendOptions = new SendOptions();
        sendOptions.RouteToThisEndpoint();

        await functionEndpoint.Send(new TriggerMessage(), sendOptions, executionContext, logger);

        return new OkObjectResult($"{nameof(TriggerMessage)} sent.");
    }
}
```

<!-- endsnippet -->

## Transport configuration constraints and limitations

The configuration API exposes NServiceBus transport configuration options via the `configuration.Transport` property to allow customization. However, not all options will be applicable for execution within Azure Functions (for more details, see [analysers](analyzers.md)).

Concurrency-related settings are controlled via the Azure Function `host.json` configuration file. See [Concurrency in Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-concurrency#service-bus) for details.



## Message consistency

NServiceBus can provide transactional consistency between incoming and outgoing messages:

<!-- snippet: asb-function-enable-sends-atomic-with-receive-with-attribute -->

```txt
[assembly: NServiceBusTriggerFunction("MyEndpoint", SendsAtomicWithReceive = true)]
```

<!-- endsnippet -->

This is equivalent to the [`SendsAtomicWithReceive`](/transports/transactions.md#transaction-modes-transport-transaction-sends-atomic-with-receive) transport transaction mode. By default, transactional consistency is disabled, providing the same transport guarantees as the [`ReceiveOnly`](/transports/transactions.md#transaction-modes-transport-transaction-receive-only) transport transaction mode.

For more information on configuring message consistency using custom triggers, refer to the [custom Azure Functions triggers](/nservicebus/hosting/azure-functions-service-bus/in-process/custom-triggers.md) documentation.

> [!NOTE]
> Microsoft.Azure.WebJobs.Extensions.ServiceBus Version 5 requires [`EnableCrossEntityTransactions`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.servicebus.servicebusoptions.enablecrossentitytransactions) to be enabled in order to support sends atomic with receive.
>
> This can be done by adding the following to `host.json`:
>
> ```
> "extensions": {
>     "ServiceBus": {
>       "EnableCrossEntityTransactions": true
>     }
>   }
> ```


## Configuration

`ServiceBusTriggeredEndpointConfiguration` loads certain configuration values from the Azure Function host environment in the following order:

- `IConfiguration` passed in via the constructor
- Environment variables

### ServiceBus connection

When using the `NServiceBusTriggerAttribute`, the connection to Azure Service Bus can be configured in multiple ways:

- Using just a `<ConnectionName>` key, with the value set to the connection string for the ServiceBus namespace to connect to.
- Using a `<CONNECTION_NAME_PREFIX>__fullyQualifiedNamespace` along with other connection properties prefixed by the same `<CONNECTION_NAME_PREFIX>`, as specified in the [Identity Based Connections](https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=blob&pivots=programming-language-csharp#common-properties-for-identity-based-connections) for Azure Functions.

If both a connection string and Identity Based connection values are specified, the connection string will take precedence. The default `<ConnectionName>` or `<CONNECTION_NAME_PREFIX>` is `AzureWebJobsServiceBus`, however an alternate value can be supplied to the `Connection` property on the `NServiceBusTriggerAttribute`

Example of a connection string based connection:

```json
{
  "IsEncrypted": false,
  "Values": {
    ...
    "AzureWebJobsServiceBus": "Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=<namespace-shared-access-key>",
  }
}
```

Example of an Identity Based connection:

```json
{
  "IsEncrypted": false,
  "Values": {
    ...
    "MyConnectionName__fullyQualifiedNamespace": "<namespace>.servicebus.windows.net",
    "MyConnectionName__tenantId": "00000000-0000-0000-0000-000000000000",
    "MyConnectionName__clientId": "00000000-0000-0000-0000-000000000000",
    "MyConnectionName__clientSecret": "<client-secret>"
  }
}
```

<!-- snippet: asb-function-isolated-identity-connection -->

```txt
[assembly: NServiceBusTriggerFunction("WorkerDemoEndpoint", Connection = "MyConnectionName")]
```

<!-- endsnippet -->

### Other Configuration

| Key                             | Value      | Notes     |
|---------------------------------|------------|-----------|
| `ENDPOINT_NAME`                 | The name of the NServiceBus endpoint to host | A value can be provided directly to the constructor. |
| `PARTICULARSOFTWARE_LICENSE`    | The NServiceBus license | Can also be provided via `serviceBusTriggeredEndpointConfig.EndpointConfiguration.License(...)` or via `NSERVICEBUS_LICENSE` environment variable for backward compatibility reasons. |
| `WEBSITE_SITE_NAME`             | The name of the Azure Function app. Provided when hosting the function in Azure. | Used to set the NServiceBus [host identifier](/nservicebus/hosting/override-hostid.md). Local machine name is used if not set. |

For local development, use `local.settings.json`. In Azure, specify a Function setting using the environment variable as the key.

```json
{
  "IsEncrypted": false,
  "Values": {
    ...
    "PARTICULARSOFTWARE_LICENSE": "<?xml version=\"1.0\" encoding=\"utf-8\"?><license id=\"1222e1d1-2222-4a46-b1c6-943c442ca710\" expiration=\"2113-11-30T00:00:00.0000000\" type=\"Standard\" LicenseType=\"Standard\" LicenseVersion=\"4.0\" MaxMessageThroughputPerSecond=\"Max\" WorkerThreads=\"Max\" AllowedNumberOfWorkerNodes=\"Max\">. . .</license>"
  }
}
```


### Custom diagnostics

[NServiceBus startup diagnostics](/nservicebus/hosting/startup-diagnostics.md) are disabled by default when using Azure Functions. Diagnostics can be written to the logs via the following snippet:

<!-- snippet: asb-enable-diagnostics -->

```cs
public override void Configure(IFunctionsHostBuilder builder)
{
    builder.UseNServiceBus(configuration =>
    {
        configuration.LogDiagnostics();
    });
}
```

<!-- endsnippet -->

Diagnostics data will be written with logger identification `StartupDiagnostics` with log level *Informational*.

### Error queue

For recoverability to move the continuously failing messages to the error queue rather than to the Azure Service Bus dead-letter queue, the error queue must be created in advance and configured using the following API:

<!-- snippet: asb-configure-error-queue -->

```cs
class EnableDiagnosticsOnStartup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.UseNServiceBus(configuration =>
        {
            configuration.AdvancedConfiguration.SendFailedMessagesTo("error");
        });
    }
}
```

<!-- endsnippet -->

## Preparing the Azure Service Bus namespace

Function endpoints cannot create their own queues or other infrastructure in the Azure Service Bus namespace.

Use the [`asb-transport` command line (CLI) tool](/transports/azure-service-bus/operational-scripting.md) to provision the entities in the namespace for the Function endpoint.

### Creating the endpoint queue

```txt
asb-transport endpoint create <queue name>
```

See the [full documentation](/transports/azure-service-bus/operational-scripting.md#available-commands-asb-transport-endpoint-create) for the `asb-transport endpoint create` command for more details.

> [!WARNING]
> If the `asb-tranport` command-line tool is not used to create the queue, it is recommended to set the `MaxDeliveryCount` setting to the maximum value.

### Subscribing to events

```txt
asb-transport endpoint subscribe <queue name> <eventtype>
```

See the [full documentation](/transports/azure-service-bus/operational-scripting.md#available-commands-asb-transport-endpoint-subscribe) for the `asb-transport endpoint subscribe` command for more details.

### Topology configuration

The [default topology](/transports/azure-service-bus/topology.md) (using topic-per-event approach) can be overridden by adding topology options to the Application configuration.

The functions integration looks for either topology options placed into `AzureServiceBus:TopologyOptions` or `AzureServiceBus:MigrationTopologyOptions` for migration scenarios.

<!-- snippet: asb-function-topology-options -->

```cs
class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.UseNServiceBus();
    }

    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        builder.ConfigurationBuilder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
    }
}
```

<!-- endsnippet -->

Using the default topology

<!-- snippet: asb-function-topology-options-json -->

```json
{
  "AzureServiceBus": {
    "TopologyOptions": {
      "PublishedEventToTopicsMap": {
        "MyNamespace.SomeEvent": "some-event"
      },
      "SubscribedEventToTopicsMap": {
        "MyNamespace.SomeEvent": [
          "some-event",
          "some-other-event"
        ]
      },
      "QueueNameToSubscriptionNameMap": {
        "Publisher": "PublisherSubscriptionName"
      }
    }
  }
}
```

<!-- endsnippet -->

Using the migration topology

<!-- snippet: asb-function-topology-migration-options-json -->

```json
{
  "AzureServiceBus": {
    "MigrationTopologyOptions": {
      "TopicToPublishTo": "TopicToPublishTo",
      "TopicToSubscribeOn": "TopicToSubscribeOn",
      "EventsToMigrateMap": [
        "MyNamespace.NotYetMigratedEvent"
      ],
      "SubscribedEventToRuleNameMap": {
        "MyNamespace.NotYetMigratedEvent": "EventRuleName"
      },
      "PublishedEventToTopicsMap": {
        "MyNamespace.MigratedEvent": "MigratedEvent"
      },
      "SubscribedEventToTopicsMap": {
        "MyNamespace.MigratedEvent": "MigratedEvent"
      },
      "QueueNameToSubscriptionNameMap": {
        "Publisher": "PublisherSubscriptionName"
      }
    }
  }
}
```

<!-- endsnippet -->

## Assembly scanning

[Assembly scanning](/nservicebus/hosting/assembly-scanning.md) loads assemblies from two locations:

- The `bin` directory of the Azure Functions application
- The Azure Functions runtime directory

If the same assembly is present in both locations, an exception is thrown which prevents the endpoint from running. Contact [Particular Software support](https://particular.net/support) for assistance.


## Package requirements

`NServiceBus.AzureFunctions.Worker.ServiceBus` requires Visual Studio 2019 and .NET SDK version `5.0.300` or higher. Older versions of the .NET SDK might display the following warning which prevents the trigger definition from being auto-generated:

```txt
CSC : warning CS8032: An instance of analyzer NServiceBus.AzureFunctions.SourceGenerator.TriggerFunctionGenerator cannot be created from NServiceBus.AzureFunctions.SourceGenerator.dll : Could not load file or assembly 'Microsoft.CodeAnalysis, Version=3.10.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified..
```

Starting in version 4.1.0 of `NServiceBus.AzureFunctions.Worker.ServiceBus` and `NServiceBus.AzureFunctions.InProcess.ServiceBus`
, warning CS8032 is treated as an error. To suppress this error, update the project's .csproj file to include the following line in the `<PropertyGroup>` section at the top:

```xml
  <WarningsAsErrors></WarningsAsErrors>
```

This will revert CS8032 to a warning status so that it does not stop the build process.
