Azure / Azure/azure-functions-host

Cannot use System.Interactive.Async v4+ in .NET Azure Function

Open
#6,336 4 comments 1 reaction 0 assignees View on GitHub
Dominant language
C#
Stars
2k
Forks
482
Avg merge
2d 12h
Merged PRs (30d)
38

Description

If you add a reference to v4.1.1 of the `System.Interactive.Async` NuGet package to an Azure Functions project, you cannot use it because the `RemoveRuntimeDependencies` build task defined in this repo deletes it from your build output.

The code that performs the deletion lives in this repo at https://github.com/Azure/azure-functions-host/blob/1ed2a54159eb3c9aaff11a9289be2bf1ce91dcb5/tools/ExtensionsMetadataGenerator/src/ExtensionsMetadataGenerator/BuildTasks/RemoveRuntimeDependencies.cs, specifically: https://github.com/Azure/azure-functions-host/blob/1ed2a54159eb3c9aaff11a9289be2bf1ce91dcb5/tools/ExtensionsMetadataGenerator/src/ExtensionsMetadataGenerator/BuildTasks/RemoveRuntimeDependencies.cs#L26-L42

The list of files this uses is also in this repo, and the offending entry is at https://github.com/Azure/azure-functions-host/blob/1ed2a54159eb3c9aaff11a9289be2bf1ce91dcb5/tools/ExtensionsMetadataGenerator/src/ExtensionsMetadataGenerator/runtimeassemblies.txt#L228

The rationale for this appears to be that Functions already brings its own copy - see https://github.com/Azure/azure-functions-host/pull/6208

But the problem is that this appears to be a v3.x era `System.Interactive.Async` which uses a definition of `IAsyncEnumerable` that is incompatible with C# 8.0's `await foreach`. The PR at https://github.com/dotnet/reactive/pull/423 modified `System.Interactive.Async` to work with the new definition of this interface. This first became available in v4.0.

The main reason I was trying to use the features in `System.Interactive.Async` at all was to be able to use `await foreach`, so being prevented from using v4.x defeats the purpose for me.

(In the project I encountered this problem I was attempting to use the `Buffer` extension method that `System.Interactive.Async` defines. This `Buffer` method is not available on `IAsyncEnumerable` unless you add a reference to `System.Interactive.Async`, and although the v3.x version supported in Functions does define `Buffer`, it does it for the wrong, old version of `IAsyncEnumerable`.)

#### Investigative information

Version information:

NuGet packages:

`Microsoft.NET.Sdk.Functions` v3.0.8
`System.Interactive.Async` v4.1.1

The local functions emulator displays this:

```
Azure Functions Core Tools (3.0.2630 Commit hash: beec61496e1c5de8aa4ba38d1884f7b48233a7ab)
Function Runtime Version: 3.0.13901.0
```

I'm not providing any cloud information because this problem occurs at build time. (It causes runtime problems, but you can see those in the local emulator)

#### Repro steps

1. Create a new Azure Functions project in Visual Studio
1. Ensure **Azure Functions v3 (.NET Core)** is selected
1 Select **Timer trigger**
1. Click **Create**
1. Right-click **Dependencies** in project in Solution Explorer, select **Manage Nuget Packages...**
1. Click **Updates** in **NuGet Package Manager**
1. Select **Microsoft.NET.Sdk.Functions**
1. On the right, select version `3.0.8` and click **Update**
1. Click **Browse** in **NuGet Package Manager**
1. In **Search** type `System.Interactive.Async`
1. Select **System.Interactive.Async** from list, then ensure a v4 era version is selected (I've chosen `4.1.1`)
1. Click **Install**, then **OK**, then **I Accept**
1. Open `Function1.cs`
1. Inside the `Run` method, add `log.LogInformation(typeof(AsyncEnumerableEx).AssemblyQualifiedName);`
1. Press **F5** to run.
1. Wait for the timer to fire. (The Visual Studio template will have set it to run once every 5 minutes.)

An error occurs.

#### Expected behavior

The expected behaviour is that when the timer fires, it displays the assembly-qualified name of the `AsyncEnumerableEx` type in the log output. In fact, that's exactly what happens if you use **Microsoft.NET.Sdk.Functions** v3.0.3:

```
[09/07/2020 16:55:00] Executing 'Function1' (Reason='Timer fired at 2020-07-09T17:55:00.0104939+01:00', Id=d021a034-46ff-4487-9fe2-68217b1e36e9)
[09/07/2020 16:55:00] C# Timer trigger function executed at: 09/07/2020 17:55:00
[09/07/2020 16:55:00] System.Linq.AsyncEnumerableEx, System.Interactive.Async, Version=4.1.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263
[09/07/2020 16:55:00] Executed 'Function1' (Succeeded, Id=d021a034-46ff-4487-9fe2-68217b1e36e9)
```

#### Actual behavior

Although it works in the emulator with the 3.0.3 `Microsoft.NET.Sdk.Functions` package, it doesn't with `3.0.8`. You get this output instead:

```
[09/07/2020 16:50:00] Executing 'Function1' (Reason='Timer fired at 2020-07-09T17:50:00.0048914+01:00', Id=4e3876f0-25a4-483f-a576-143529058cc3)
[09/07/2020 16:50:02] Executed 'Function1' (Failed, Id=4e3876f0-25a4-483f-a576-143529058cc3)
[09/07/2020 16:50:02] System.Private.CoreLib: Exception while executing function: Function1. FunctionsInteractiveAsyncRepro: Could not load file or assembly 'System.Interactive.Async, Version=4.1.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263'. The system cannot find the file specified.
```

Contributor guide

Open the contributing guide

Research direction

Start with tools/ExtensionsMetadataGenerator/src/ExtensionsMetadataGenerator/BuildTasks/RemoveRuntimeDependencies.cs and the System.Interactive.Async entry in runtimeassemblies.txt. Run the supplied Azure Functions v3 reproduction with Microsoft.NET.Sdk.Functions 3.0.8 and System.Interactive.Async 4.1.1, then compare the build output and emulator logs. Done means the v4 assembly remains available and the timer function produces the expected assembly-qualified name.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp
Domain
backend, build-system, cloud
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.