【Azure Functions】BlobTriggerなんもわからん

今回はいつも以上にまとまりがありませんぞ。


[Azure Functions](https://azure.microsoft.com/ja-jp/products/functions)は[様々な起動タイミングを設定](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-triggers-bindings?tabs=isolated-process%2Cpython-v2&pivots=programming-language-csharp)することができます。
HTTPリクエストが来た、定期実行、ファイルがアップロードされた、データベースにデータが保存された、などのイベントで起動できるのでたいへん便利です。

それはいいんだけど、Azure Functionsは[ドキュメント](https://learn.microsoft.com/ja-jp/azure/azure-functions/)が無駄にたくさんあるわりに中身が不親切なんですよね。
今回C#で[BLOBに保存したタイミングのトリガー](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-storage-blob-trigger?tabs=python-v2%2Cisolated-process%2Cnodejs-v4&pivots=programming-language-csharp)を実装したかったのですが、このドキュメントをそのままコピペすると『型または名前空間の名前 'BlobTrigger' が見つかりませんでした』とか言われて動きませんでした。
これの解決にやたら苦戦したので記録しておきます。

ていうかAzureのドキュメント、全体的に何言ってるのかわからないテキストが多い。


# xxxxxx.csproj

`<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="5.2.1" />`を追加する。

Versionは[nugetをコピペ](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Storage/)しただけなので、コピペするタイミングによって異なる場合があります。

# xxxxxx.cs

`using Microsoft.Azure.WebJobs;`を追加する。

これで`[BlobTrigger("test-samples-trigger/{name}")] string myTriggerItem`が動くようになります。

PackageReferenceと全然違うやん。
どうして`using Microsoft.Azure.WebJobs.Extensions.Storage;`とかでないのかはわかりません。
これドキュメントのどこにも書かれてないんですが、どうやって辿り着けばいいんだ？

そもそもドキュメントには`using`が全く書かれていないので、だいたいいつもクラスがないって引っかかるんですよね。
パッケージ名クラス名でググれば概ね解決することが多いのですが、今回のBlobTriggerは何故か全然ひっかからなくて、どのパッケージにあるのか見つけるのにやたら苦労しました。


# 本当にMicrosoft.Azure.WebJobs.Extensions.Storageでいいの？

[似たような別のドキュメント](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-storage-blob?tabs=isolated-process%2Cextensionv5%2Cextensionv3&pivots=programming-language-csharp)には[Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs/)とかいうまた違うパッケージが出てきています。
さらに[謎のマイグレーションガイド](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/AzureStorageNetMigrationV12.md)に出てくるのは[Microsoft.Azure.Storage.Blob](https://www.nuget.org/packages/Microsoft.Azure.Storage.Blob/)と[Azure.Storage.Blobs](https://www.nuget.org/packages/Azure.Storage.Blobs)です。

なにがどう違うんだこれ。


# BLOBトリガーのサンプルが意味不明

[ドキュメント記載のサンプル](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-storage-blob-trigger?tabs=python-v2%2Cisolated-process%2Cnodejs-v4&pivots=programming-language-csharp#example)がこちらです。

```csharp
public static class BlobFunction
{
    [Function(nameof(BlobFunction))]
    [BlobOutput("test-samples-output/{name}-output.txt")]
    public static string Run(
        [BlobTrigger("test-samples-trigger/{name}")] string myTriggerItem,
        [BlobInput("test-samples-input/sample1.txt")] string myBlob,
        FunctionContext context)
    {
        var logger = context.GetLogger("BlobFunction");
        logger.LogInformation("Triggered Item = {myTriggerItem}", myTriggerItem);
        logger.LogInformation("Input Item = {myBlob}", myBlob);

        // Blob Output
        return "blob-output content";
    }
}
```

`"test-samples-trigger/{name}"`にBLOBが保存されたことをトリガーにして、`"test-samples-input/sample1.txt"`を読み込んで`"test-samples-output/{name}-output.txt"`に出力します。

なんで？

普通`"test-samples-trigger/{name}"`をどうこうしたいだろ。
というか一番欲しい情報は`{name}`だと思うのだけど、それを取り出す方法が何故かここには書かれていません。
わざわざ[別ページまで辿る](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-expressions-patterns)必要があります。


```csharp
public static class BlobFunction
{
    [Function(nameof(BlobFunction))]
    [BlobOutput("test-samples-output/{name}-output.txt")]
    public static string Run(
        [BlobTrigger("test-samples-trigger/{name}")] string myTriggerItem,
        [BlobInput("test-samples-input/sample1.txt")] string myBlob,
        string name,
        FunctionContext context)
    {
        // 引数nameに入る
        return name;
    }
}
```

トリガーに`"test-samples-trigger/{name}"`とあった場合、引数に`string name`を増やすとどういうわけかそこに値が入ってきます。
この取り出し方を真っ先に書いてほしいのだが。


```csharp
public static class BlobFunction
{
    [Function(nameof(BlobFunction))]
    public static string Run(
        [BlobTrigger("{foo}/{bar}/{name}")] string myTriggerItem,
        string foo,
        string bar,
        string baz,
        ILogger logger,
        IBinder binder)
    {
        return $"foo:{foo} bar:{bar} baz:{baz}";
    }
}
```

取り出したい値が複数ある場合は、単に引数を増やせばいいです。
またBlobInputやBlobOutputなどは、いらない場合は引数から消すだけで使わないようになります。
さらに引数にILoggerやらIBinderを指定すると、なんか勝手にそれらがバインドされてきます。

引数がアバウトすぎてやべえ。
この引数に使える値の一覧どこにあるんだ？

# BlobOutputに動的値を使えない

```csharp
public static class BlobFunction
{
    [Function(nameof(BlobFunction))]
    [BlobOutput("test-samples-output/{name.padLeft(10,'0')}-output.txt")]
    public static string Run(
        [BlobTrigger("test-samples-trigger/{name}")] string myTriggerItem,
        [BlobInput("test-samples-input/sample1.txt")] string myBlob,
        string name)
    {
        return name;
    }
}
```

動きません。
`BlobOutput`の`"test-samples-output/{name.padLeft(10,'0')}-output.txt"`は文字列補間としては正しい形式であるにも関わらず、ここではエラーになります。
`BlobOutput`には`BlobInput`でバインドした値をそのまま使うことはできますが、計算式などを使うことはできません。
なんで。

このあたり有効な書式とか[Azure Functions における Azure Blob Storage のバインドの概要](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-storage-blob?tabs=isolated-process%2Cextensionv5%2Cextensionv3&pivots=programming-language-csharp)にも[Azure Functions における Azure Blob Storage の出力バインド](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-storage-blob-output?tabs=python-v2%2Cisolated-process%2Cnodejs-v4&pivots=programming-language-csharp)にも一切記載がありませんでした。

どうしても動的値を使いたい場合は、`BlobOutput`等を使わず自力で接続しに行くか、[IBinderを使う](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-dotnet-class-library?tabs=v4%2Ccmd#binding-at-runtime)くらいしかなさそうでした。

あとリクエストによってはBlobOutputを出力したくない場合もあると思うんだけど、`BlobOutput`を書いた上で`BlobOutput`に対して何もしなかったときは空のファイルが出力されてしまいました。
あるときはファイルを出力したい、また他のあるときは出力したくない、といった場合はどうすればいいんだろう。