I have read several guides on the subject, with no success. The following guide gave me a lot of hints about a setup that I was simply not able to figure nor find in the official docs:
https://codetraveler.io/2021/05/28/creating-azure-functions-using-net-5/
But I'm still struggling to find some piece(s) to make it run.
This is the message I get when run locally:
No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.). For detailed output, run func with --verbose flag.
This message includes something that I suspect missing, but I don't know how to solve: the ServiceBus part. I am not sure if the function has enough information to connect to the ServiceBus. It is in the config file, but I don't know where the function knows what part of the configuration to use.
This is my function:
public class MyFunction
{
private readonly IService1 service;
public MyFunction(IService1 service)
{
this.service = service;
}
[FunctionName("Function1")]
public async Task ProcessMessage([ServiceBusTrigger("topic", "subscription", Connection = "ServiceBusConnectionString")] Message message)
{
await service.Process(message);
}
}
This is my appsettings.json config file:
{
"ConnectionStrings": {
"ServiceBusConnectionString": "Endpoint=sb://****.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=******"
}
}
Some local.settings.json (I guess it makes possible to run in local machine)
{
"IsEncrypted": false,
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
The Program class:
public class Program
{
public static Task Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
var builder = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices((context, services) =>
{
services.AddTransient<IService1, service>();
});
var host = builder.Build();
return host.RunAsync();
}
}
I also have modified the project file to contain:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
</PropertyGroup>
And:
<ItemGroup>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Also added a few Nuget packages:
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.5.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="4.2.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.0.4" OutputItemType="Analyzer" />
Oh man... that's a huge setup! I hope future versions make it a lot easier!!!
Looking at your function code, I am making an educated guess that you are mixing in-proc and out of process code.
To fix this. Follow these steps.
Out of process/isolated process model should not be using any reference to Microsoft.Azure.WebJobs.*
packages, such as Microsoft.Azure.WebJobs.Extensions.ServiceBus
. Instead you should be referencing the Microsoft.Azure.Functions.Worker.Extensions.ServiceBus
package.
Use the Function
attribute instead of the FunctionName
attribute. FunctionName
comes from the Microsoft.Azure.WebJobs
which we should not be using in out-of-process/isolated process function apps.
Your function code will look like below after the change.
public class MyFunction
{
private readonly IService1 service;
private readonly ILogger<MyFunction> logger;
public MyFunction(IService1 service, ILogger<MyFunction> logger)
{
this.service = service ?? throw new ArgumentNullException(nameof(service));
this.logger = logger?? throw new ArgumentNullException(nameof(logger));
}
[Function("Function1")]
public async Task ProcessMessage([ServiceBusTrigger("mytopic",
"mysubscription",
Connection = "ServiceBusConnectionString")] object message)
{
logger.LogInformation($"ServiceBus OOP function processed: {message}");
await service.Process(message);
}
}
The ServiceBusTrigger
attribute's Connection
property value is the name of your app settings entry for specifying the service bus connection string. For local running, you should keep it in your local.settings.json
file, inside the Values
section. You can add a new entry(after your existing FUNCTIONS_WORKER_RUNTIME
entry) like below.
{
"Values": {
"ServiceBusConnectionString": "PutYourServiceBusConnStringHere"
}
}
When you have to run this in prod, you will go to your function app resource and add a new app settings entry under Settings -> Configuration. The name for the setting entry will be ServiceBusConnectionString
. The function run time will read this and use that value.
I strongly suggest you to create a new "azure functions" project in Visual studio and select Isolated process model & Service bus trigger. That will create a new project for you with all the minimal pieces you need for a working service bus trigger.