2

I have a C# queue trigger function that has to run async because it awaits async methods to both read and add data to tables in storage. I need to be able to exit this function early in the event of certain conditions arising, but can't figure out how to either (a) return correctly, or (b) terminate just the one instance of the function.

I originally had it coded as follows. My function works perfectly and does what it is supposed to do:

public static async void Run([QueueTrigger("myqueue"), StorageAccount("mystorge")] String transdata,

The problem is that the runtime doesn't usually like the idea of async void, so Azure gives me an error:

System.InvalidOperationException : Functions must return Task or void, have a binding attribute for the return value, or be triggered by a binding that natively supports return values.

It clearly says right there that you can return void but obviously it doesn't actually let you, thus the error. It says the function must return Task, so I re-coded as follows:

public static async Task Run([QueueTrigger("myqueue"), StorageAccount("mystorage")] string transdata,

That didn't work, so next I tried this:

public static async Task<Task> Run([QueueTrigger("myqueue"), StorageAccount("mystorage")] string transdata,

And tried returning early like this:

return Task.CompletedTask;

But that throws a completely different error:

Microsoft.Azure.WebJobs.Host: Cannot bind parameter '$return' to type Task&. Make sure the parameter Type is supported by the binding.

I don't NEED to return any value. I just need to end processing. So I tried finding some way to simply end the function instead of returning, and found this post which says that although you can do Environment.Exit it will end other processes as well, so that's a no-go.

And a few extra tidbits of information in case it's relevant:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.2" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.9" />
    <PackageReference Include="Twilio" Version="5.56.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

This example from Microsoft (non-Async) demonstrates using Task<string>, so I decided to try Task<bool> with return false;, but that generated a similar error:

Microsoft.Azure.WebJobs.Host: Cannot bind parameter '$return' to type Boolean&.

And doing return Task.FromResult(false); doesn't work - the IDE says that because it's Async it has to return bool instead of Task<bool>.

How do you simply end processing of your queue trigger function, or return correctly?

UPDATE 1: 2021-Jan-09

Although I'd already tried what has been suggested, I thought that perhaps I'd made a mistake so I should try it again. So I declared the queue trigger like this:

public static async Task Run([QueueTrigger("myqueue"), StorageAccount("mystorage")] String transdata,

And simply return normally if needed based on conditions that may arise during processing:

return;

But get the message I reported already:

Microsoft.Azure.WebJobs.Host: Error indexing method 'AddToLicenseQueue'. Microsoft.Azure.WebJobs.Host: Functions must return Task or void, have a binding attribute for the return value, or be triggered by a binding that natively supports return values.

If you leave out static, the IDE red-underscores run and says,

'Run': cannot declare instance members in a static class.

static is standard in the stock/shell queue trigger function if you create a new one in Visual Studio or VS Code, or even look at Microsoft's website.

technonaut
  • 484
  • 3
  • 12

1 Answers1

1

Your confusion is about async/await.

If you mark a method as async, then that method should return a Task (for void) and Task<ReturnType> for a method that returns a type.

public Task NonAsyncMethodReturnsTask()
{
   // no calls to "await"
   return Task.CompletedTask;
}

For async methods, returning a Task is the same as returning void. Notice how this method makes an await call.

public async Task AsyncMethodReturnsVoid()
{
   bool needToReturnEarly = false;
   if (needToReturnEarly)
   {
      return;
   }       

   // calls await
   await Task.Delay(0);
}

In the example from Microsoft, this is a non-async method with a return value:

public static Task<string> Run(_)
{
    string json = string.Format("{{ \"id\": \"{0}\" }}", input.Id);
   
    return Task.FromResult(json);
}

And for completeness, here is an async method with a return value:

public async Task<string> AsyncMethodReturnsString()
{
   // await call
   await Task.Delay(0);
   string returnValue = "some string";

   // Note: we don't need to Task.FromResult(returnValue);
   return returnValue;
}

If your method is making await calls, then you should follow the "AsyncMethodReturnsVoid" syntax. If your method is not making any await calls then you should remove the async and just return void.

Steve Wong
  • 2,038
  • 16
  • 23
  • It has to run async since I await a couple of different async methods working with tables in the storage account. In your second block of code, which seems to fit my situation, you don't demonstrate how to exit the routine early and correctly in an Azure function queue trigger app, which is my problem. If I use `public static async Task Run` and then `return;`, Azure generates error: (cont'd in next message) – technonaut Jan 09 '22 at 14:33
  • `Error indexing method 'AddToLicenseQueue'. Microsoft.Azure.WebJobs.Host: Functions must return Task or void, have a binding attribute for the return value, or be triggered by a binding that natively supports return values.` I've never set up a binding for a return attribute - perhaps that's the missing link, so I spent 2 hours researching that yesterday and didn't find any examples fitting this situation. – technonaut Jan 09 '22 at 14:36
  • Also, upon normal completion, I realize that I don't need a return statement at all. My issue is with how to return early if certain conditions arise. – technonaut Jan 09 '22 at 14:41
  • @technonaut I think second block of this answer should work and also remove static from class and mehtods – sa-es-ir Jan 09 '22 at 16:53
  • That doesn't work; I just tried and get the same error I reported initially. I did add an update with the statements used. – technonaut Jan 09 '22 at 19:52
  • Hold the phone, may have an update on this. – technonaut Jan 09 '22 at 20:20