Custom Azure Functions triggers

If the trigger function must be customized, disable generation of the trigger function by removing the NServiceBusTriggerFunction attribute. A custom trigger function can then be added manually to the project:

class CustomTriggerDefinition
{
    IFunctionEndpoint functionEndpoint;

    public CustomTriggerDefinition(IFunctionEndpoint functionEndpoint)
    {
        this.functionEndpoint = functionEndpoint;
    }

    [FunctionName("MyCustomTrigger")]
    public async Task Run(
        [ServiceBusTrigger("MyFunctionsEndpoint")]
        Message message,
        ILogger logger,
        MessageReceiver messageReceiver,
        ExecutionContext executionContext)
    {
        await functionEndpoint.Process(message, executionContext, messageReceiver, logger);
    }
}

Configuring transaction mode

If the NServiceBusTriggerFunction attribute is not used, IFunctionEndpoint.Process will determine the transaction mode based on the ServiceBusTrigger's AutoComplete property:

If auto-complete is enabled, which is the default, NServiceBus can't control the receive transaction and the message is processed in TransportTransactionMode.ReceiveOnly mode.

[FunctionName("ProcessMessage")]
public async Task Run(
    // Setting AutoComplete to true (the default) processes the message non-transactionally
    [ServiceBusTrigger("ProcessMessage", AutoComplete = true)]
    Message message,
    ILogger logger,
    MessageReceiver messageReceiver,
    ExecutionContext executionContext)
{
    await endpoint.Process(message, executionContext, messageReceiver, logger);
}

If auto-complete is disabled, NServiceBus can fully control incoming and outgoing messages and the message is processed in TransportTransactionMode.SendsAtomicWithReceive mode.

[FunctionName("ProcessMessageTx")]
public async Task RunTx(
    // Setting AutoComplete to false processes the message transactionally
    [ServiceBusTrigger("ProcessMessageTx", AutoComplete = false)]
    Message message,
    ILogger logger,
    MessageReceiver messageReceiver,
    ExecutionContext executionContext)
{
    await endpoint.Process(message, executionContext, messageReceiver, logger);
}

If additional control is needed, or the service bus trigger is not configured using an attribute, use the concrete FunctionEndpoint class:

class MyFunctions
{
    const bool EnableTransactions = true;

    // NOTE: Use concrete class instead of interface
    readonly FunctionEndpoint endpoint;

    public MyFunctions(FunctionEndpoint endpoint)
    {
        this.endpoint = endpoint;
    }

    [FunctionName("ProcessMessages")]
    public async Task Run(
        [ServiceBusTrigger("ProcessMessages", AutoComplete = !EnableTransactions)]
        Message message,
        ILogger logger,
        MessageReceiver messageReceiver,
        ExecutionContext executionContext)
    {
        if(EnableTransactions)
        {
            await endpoint.ProcessTransactional(message, executionContext, messageReceiver, logger);
        }
        else
        {
            await endpoint.ProcessNonTransactional(message, executionContext, messageReceiver, logger);
        }
    }
}
Incorrectly configuring the service bus trigger auto-complete setting can lead to message loss. Use the auto-detection mechanism on the function endpoint interface, or use the trigger function attribute to specify message consistency.

Related Articles


Last modified