Why custom trigger definitions not supported for isolated worker?

Documnentation for isolated worker says “Custom trigger definitions for Azure Service Bus triggers using NServiceBus message handlers are currently not supported.” Anyone please help me to understand why it is not supported for isolated.

That was a decision made when implementing support for the isolated worker hosting model because wiring up custom triggers correctly was more error-prone compared to using the auto-generated triggers. Custom triggers aren’t impossible though, they are just not neatly integrated at the moment. Why would you like to use custom triggers in the first place?

I would like to read my endpoint name from the configuration rather than hardcoding it in the attribute. Is there any way I can achieve this?

You should be able to customize the endpoint name via IConfiguration or Environmentvariables by setting the ENDPOINT_NAME setting before starting the host, see Isolated Worker • NServiceBus.AzureFunctions.Worker.ServiceBus • Particular Docs. Would that work for you?

It is not working for me. Even if I set ENDPOINT_NAME in the configuration, it is looking for the endpoint name given in the assembly attribute and throwing messaging entity not found error at function startup.

I think in that case you can remove the attribute completely. There should also be an UseNServiceBus overload on the host builder extension that allows you to pass a custom endpoint name instead, if that works better for you.

But if I removed the attribute, no trigger function would be generated, right?. That’s why I tried to use a custom trigger, but it’s not supported either. Yes, I can use the overload to pass the endpoint name. Still, the function is looking for the endpoint specified in the attribute.

If you want to use your custom trigger definition, you can use the code that would otherwise be generated by the code generator:

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using NServiceBus;

public class FunctionEndpointTrigger
{
    IFunctionEndpoint endpoint;

    public FunctionEndpointTrigger(IFunctionEndpoint endpoint)
    {
        {
            this.endpoint = endpoint;
        }
    }

    [Function("CustomTrigger")]
    public async Task Run(
        [ServiceBusTrigger(QUEUENAME)] byte[] messageBody,
        IDictionary<string, object> userProperties,
        string messageId,
        int deliveryCount,
        string replyTo,
        string correlationId,
        FunctionContext context)
    {
        {
            var legacyUserProperties = new Dictionary<string, string>(userProperties.Count);
            foreach (var userProperty in userProperties)
            {
                {
                    legacyUserProperties[userProperty.Key] = userProperty.Value.ToString();
                }
            }

            await endpoint.Process(messageBody, legacyUserProperties, messageId, deliveryCount, replyTo, correlationId, context);
        }
    }
}

Then you can pass the values as you see fit. You’ll have to be careful to configure the correct endpoint name using the mentioned env variable or the UseNServiceBus(string endpointname) overload to be aligned with the configured trigger queue name. Would that unblock you?

@Tim why is it legacy?

var legacyUserProperties = new Dictionary<string, string>(userProperties.Count);

But we don’t want to use a custom trigger definition. We only want to set the endpoint from the configuration, instead of being hardcoded.

I have the same problem and was confused about the documentation. Putting ENDPOINT_NAME in the config doesn’t work. The attribute always wins. Can we use the attribute like this?

[assembly: NServiceBusTriggerFunction("%ENDPOINT_NAME%")]

If we can’t override the endpoint name, is this not a bug?

1 Like

You can’t. Isolated worker SDK doesn’t support imperative binding. Nothing to do with NServiceBus.

To be honest, I don’t have the detailed context to explain how that variable naming came together. Are you suggesting that “legacy” is inappropriate here?

@remyvd as Sean explained, I think NServiceBus is limited by the capabilities of the Azure Functions SDK itself here. Have you been able to achieve your goals using the plain Azure Functions SDK (without NServiceBus)?

I have the same problem and was confused about the documentation. Putting ENDPOINT_NAME in the config doesn’t work. The attribute always wins. Can we use the attribute like this?

It might be, that the documentation is not very clear here. The ENDPOINT_NAME variable might only relevant when not using the attribute (which we don’t really recommend at the moment). I’ll look into clarifying this documentation.

I don’t think this falls under bindings. This is injecting a config value.

This code works in an isolated worker:

[Function("CheckForUpdatedContactsFunction")]
public async Task Run([TimerTrigger("%CheckForUpdatedContactsSchedule%", RunOnStartup = RunOnStartup)] TimerInfo timerInfo, FunctionContext context)
{
      // code
}

Update:
Ok, after reading the documentation, I see that apparently app settings are also a binding type.

@Tim I saw the word ‘legacy’ and it triggered my question.

@remyvd Could you share the Microsoft documentation you’re looking at?

I found this closed GitHub issue which points to Azure Functions triggers and bindings concepts when it was closed, and says there’s a section called “Resolving app settings” but that section no longer exists.

I’m not sure. I thought I read that App Settings on itself are also a binding type. Can’t find it now anymore.

I can only find the documentation about Binding expressions:

Interesting. At least that’s some evidence that the whole binding syntax is supposed to work. I was a little concerned that the feature had been removed and all the documentation had been scrubbed.

[assembly:NServiceBusTriggerFunction(“%ENDPOINT_NAME%”, TriggerFunctionName = “MyFunctionName”)]

The above worked for me, where I could specify the value for ENDPOINT_NAME in the local.settings.config file. (Any key other than “ENDPOINT_NAME” will not work).

Function will listen to the queue specified in the value for “ENDPOINT_NAME” in local.settings.config file .

Also, TriggerFunctionName property gives control over the function name that will be generated. We must specify name here, otherwise it will be generated with special characters like “NServiceBusFunctionEndpointTrigger-%ENDPOINT_NAME%”, which will run fine locally but cannot be deployed to azure. TriggerFunctionName cannot be config driven.

following is the zip file containing code.

@Rishabh_Ajmera thanks for this tip! I changed this and deployed it. So far, so good!

@Tim or @DavidBoike I think it would be handy to update the documentation of the following page:

That when you want to use the config value “ENDPOINT_NAME”, you need to add the TriggerFunctionName, like:
[assembly: NServiceBusTriggerFunction("%ENDPOINT_NAME%", TriggerFunctionName="NServiceBusTriggerFunction")]