M1 Mac で Azure Durable Functions の分離ワーカーモデルが Grpc.Core: Error loading native library. エラー

インプロセスモデルが延命されたとは言え、方針は分離ワーカーモデル一本のようなので、これから Azure Functions を新規に作成するなら分離ワーカーモデルで作成しておくのが良い選択だと信じています。そこで、自宅の M1 Mac で Durable Functions を検証がてら試してみました。ところが .NET6 でも .NET 8 でも同じエラーが発生しました。

## .NET 8 で分離ワーカーの Durable Functions 作成

```zsh:zsh
func init mnrdf8 --worker-runtime=dotnet-isolated --target-framework=net8.0

cd mnrdf8

func new --name Example --template DurableFunctionsOrchestration

func start
```

下記のように `Grpc.Core: Error loading native library.` エラーが発生

```txt
[2024-09-21T11:51:37.936Z] Found /Users/mnr/work/test/mnrdf8/mnrdf8.csproj. Using for user secrets file configuration.
[2024-09-21T11:51:39.288Z] A host error has occurred during startup operation '28c4f96b-c3da-4601-9747-f689dcbce0ad'.
[2024-09-21T11:51:39.288Z] Grpc.Core: Error loading native library. Not found in any of the possible locations: /Users/mnr/work/test/mnrdf8/bin/output/.azurefunctions/libgrpc_csharp_ext.arm64.dylib,/Users/mnr/work/test/mnrdf8/bin/output/.azurefunctions/runtimes/osx-arm64/native/libgrpc_csharp_ext.arm64.dylib,/Users/mnr/work/test/mnrdf8/bin/output/.azurefunctions/../../runtimes/osx-arm64/native/libgrpc_csharp_ext.arm64.dylib.
Value cannot be null. (Parameter 'provider')
[2024-09-21T11:51:39.322Z] Host startup operation has been canceled
```

## mnrdf8.csproj に追記

`Contrib.Grpc.Core.M1` パッケージの 2.41.0 を追加

```xml:xml
    <PackageReference Include="Contrib.Grpc.Core.M1" Version="2.41.0" />
```

Mac の時だけ `libgrpc_csharp_ext.arm64.dylib` をコピー

```xml:xml
  <Target Name="CopyGrpcNativeAssetsToOutDir" AfterTargets="Build">
    <ItemGroup>
      <NativeAssetToCopy Condition="$([MSBuild]::IsOSPlatform('OSX'))" Include="$(OutDir)runtimes/osx-arm64/native/*" />
    </ItemGroup>
    <Copy SourceFiles="@(NativeAssetToCopy)" DestinationFolder="$(OutDir).azurefunctions/runtimes/osx-arm64/native" />
  </Target>
```

mnrdf8.csproj 全体

```xml:mnrdf8.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <OutputType>Exe</OutputType>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.21.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.1.1" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.2.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.0" />
    <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.2.0" />
    <PackageReference Include="Contrib.Grpc.Core.M1" Version="2.41.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
  <ItemGroup>
    <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
  </ItemGroup>
  <Target Name="CopyGrpcNativeAssetsToOutDir" AfterTargets="Build">
    <ItemGroup>
      <NativeAssetToCopy Condition="$([MSBuild]::IsOSPlatform('OSX'))" Include="$(OutDir)runtimes/osx-arm64/native/*" />
    </ItemGroup>
    <Copy SourceFiles="@(NativeAssetToCopy)" DestinationFolder="$(OutDir).azurefunctions/runtimes/osx-arm64/native" />
  </Target>
</Project>
```

## Functions を再実行

```zsh:zsh
func start
```

```txt
[2024-09-21T11:54:12.637Z] Found /Users/mnr/work/test/mnrdf8/mnrdf8.csproj. Using for user secrets file configuration.
[2024-09-21T11:54:15.249Z] Worker process started and initialized.

Functions:

        Example_HttpStart: [GET,POST] http://localhost:7071/api/Example_HttpStart

        Example: orchestrationTrigger

        SayHello: activityTrigger
```

## 参考

https://learn.microsoft.com/ja-jp/azure/azure-functions/durable/durable-functions-dotnet-isolated-overview#why-use-durable-functions-in-the-net-isolated-worker

https://github.com/microsoft/durabletask-dotnet/issues/148