This sample shows how to host NServiceBus within an Azure Function, in this case, a function triggered by an incoming Service Bus message. This enables hosting message handlers in Azure Functions, gaining the abstraction of message handlers implemented using IHandleMessages
and also taking advantage of NServiceBus's extensible message processing pipeline.
When hosting NServiceBus within Azure Functions, the Function (as identified by the [FunctionName]
attribute) hosts an NServiceBus endpoint that is capable of processing different message types.
The Azure Functions SDK enforces certain constraints that are also applied to NServiceBus endpoints. Review these constraints before running the sample.
Prerequisites
Manually create queue
Unlike a traditional NServiceBus endpoint, an endpoint hosted in Azure Functions cannot create its own input queue. In this sample, that queue name is ASBTriggerQueue
.
To create the endpoint with the Azure Service Bus Transport CLI, execute the following command:
asb-transport endpoint create ASBTriggerQueue
The command will create the required queue and topic.
Configure Connection string
To use the sample, a valid Service Bus connection string must be provided in the local.
file.
Sample structure
The sample contains the following projects:
AzureFunctions.
- NServiceBus endpointASBTrigger. FunctionsHostBuilder AzureFunctions.
- message definitionsMessages
Running the sample
The Functions project contains two functions:
- An auto-generated Service Bus-triggered function. This function is generated by the
NServiceBusTriggerFunction
attribute in theStartup.
file.cs - An HTTP-triggered function to send a message to the Azure Service Bus queue.
Running the sample will launch the Azure Functions runtime window.
To try the Azure Function:
- Open a browser and navigate to http://localhost:7071/api/HttpSender. The port number might be different and will be indicated when the function project is started.
- The queue-triggered function will receive the
TriggerMessage
and process it with NServiceBus. - The NServiceBus message handler for
TriggerMessage
sends aFollowUpMessage
. - The queue-triggered function will receive the
FollowUpMessage
and process it with NServiceBus.
Code walk-through
The NServiceBus endpoint configured using IFunctionHostBuilder
in the Startup
class like this:
[assembly: FunctionsStartup(typeof(Startup))]
[assembly: NServiceBusTriggerFunction(Startup.EndpointName, SendsAtomicWithReceive = true)]
public class Startup : FunctionsStartup
{
public const string EndpointName = "ASBTriggerQueue";
public override void Configure(IFunctionsHostBuilder builder)
{
var services = builder.Services;
// register custom service in the container
services.AddSingleton(_ =>
{
var configurationRoot = builder.GetContext().Configuration;
var customComponentInitializationValue = configurationRoot.GetValue<string>("CustomComponentValue");
return new CustomComponent(customComponentInitializationValue);
});
builder.UseNServiceBus(() => new ServiceBusTriggeredEndpointConfiguration(EndpointName));
}
}
Note the NServiceBusTriggerFunction
is used to automatically generate the Azure Functions trigger code that is needed to invoke NServiceBus.
Handlers
These are the message handlers, with a CustomDependency
passed in.
public class TriggerMessageHandler : IHandleMessages<TriggerMessage>
{
static readonly ILog Log = LogManager.GetLogger<TriggerMessageHandler>();
readonly CustomComponent customComponent;
public TriggerMessageHandler(CustomComponent customComponent)
{
this.customComponent = customComponent;
}
public Task Handle(TriggerMessage message, IMessageHandlerContext context)
{
Log.Warn($"Handling {nameof(TriggerMessage)} in {nameof(TriggerMessageHandler)}");
Log.Warn($"Custom component returned: {customComponent.GetValue()}");
return context.SendLocal(new FollowupMessage());
}
}
public class FollowupMessageHandler : IHandleMessages<FollowupMessage>
{
static readonly ILog Log = LogManager.GetLogger<FollowupMessageHandler>();
readonly CustomComponent customComponent;
public FollowupMessageHandler(CustomComponent customComponent)
{
this.customComponent = customComponent;
}
public Task Handle(FollowupMessage message, IMessageHandlerContext context)
{
Log.Warn($"Handling {nameof(FollowupMessage)} in {nameof(FollowupMessageHandler)}.");
Log.Warn($"Custom component returned: {customComponent.GetValue()}");
return Task.CompletedTask;
}
}