Azure Functions C# developer reference (Azure Functions C# 開発者向けリファレンス)

Azure Functions の C# エクスペリエンスは、Azure WebJobs SDK に基づいています。 データは、メソッドの引数を使用して C# 関数に渡されます。 引数名は function.jsonで指定され、関数のロガーやキャンセル トークンなどにアクセスするための定義済みの名前があります。

この記事では、「 Azure Functions developer reference (Azure Functions 開発者向けリファレンス)」を既に読んでいることを前提としています。

.csx のしくみ

.csx の形式では、"定型" の記述が少なく、C# 関数のみの記述に重点が置かれています。 Azure Functions の場合、通常どおりに上部に表示する任意のアセンブリ参照と名前空間を含め、すべてを名前空間とクラスにラッピングする代わりに、 Run メソッドの定義のみを行います。 Plain Old CLR Object (POCO) オブジェクトを定義する場合など、クラスを含める必要がある場合は、同じファイル内にクラスを含めることができます。

引数へのバインド

function.json 構成の name プロパティを通じて、さまざまなバインドが C# 関数にバインドされます。 各バインドには、バンドごとに記述される、独自のサポートされている型があります。たとえば、blob のトリガーでは、文字列、POCO、その他いくつかの型がサポートされます。 ニーズに合わせて最適な型を使用できます。 POCO オブジェクトでは、各プロパティに getter と setter が定義されている必要があります。

public static void Run(string myBlob, out MyClass myQueueItem)
{
    log.Verbose($"C# Blob trigger function processed: {myBlob}");
    myQueueItem = new MyClass() { Id = "myid" };
}

public class MyClass
{
    public string Id { get; set; }
}
ヒント

HTTP または WebHook のバインディングを使用する場合は、HTTPClient に関するこのベスト プラクティスのドキュメントを参照することをお勧めします。

ログの記録

出力を C# のストリーミング ログにログ記録するために、 TraceWriter 型の引数を含めることができます。 これの名前を logにすることをお勧めします。 Azure Functions の Console.Write は避けることをお勧めします。

public static void Run(string myBlob, TraceWriter log)
{
    log.Info($"C# Blob trigger function processed: {myBlob}");
}

非同期

関数を非同期にするには、async キーワードを使用して Task オブジェクトを返します。

public async static Task ProcessQueueMessageAsync(
        string blobName, 
        Stream blobInput,
        Stream blobOutput)
    {
        await blobInput.CopyToAsync(blobOutput, 4096, token);
    }

キャンセル トークン

場合によっては、シャット ダウンに影響を受ける操作があります。 クラッシュに対応するコードを記述するのが最善の方法ですが、グレースフル シャットダウンの要求を処理する場合は、CancellationToken 型の引数を定義します。 ホストのシャットダウンがトリガーされると、 CancellationToken が提供されます。

public async static Task ProcessQueueMessageAsyncCancellationToken(
        string blobName, 
        Stream blobInput,
        Stream blobOutput,
        CancellationToken token)
    {
        await blobInput.CopyToAsync(blobOutput, 4096, token);
    }

名前空間のインポート

名前空間をインポートする必要がある場合は、 using 句を使用して、通常どおりにインポートできます。

using System.Net;
using System.Threading.Tasks;

public static Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)

次の名前空間は自動的にインポートされるため、オプションとなります。

  • System
  • System.Collections.Generic
  • System.IO
  • System.Linq
  • System.Net.Http
  • System.Threading.Tasks
  • Microsoft.Azure.WebJobs
  • Microsoft.Azure.WebJobs.Host

外部アセンブリの参照

フレームワークのアセンブリには、 #r "AssemblyName" ディレクティブを使用して参照を追加します。

#r "System.Web.Http"

using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

public static Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)

次のアセンブリは、Azure Functions をホストしている環境によって自動的に追加されます。

  • mscorlib,
  • System
  • System.Core
  • System.Xml
  • System.Net.Http
  • Microsoft.Azure.WebJobs
  • Microsoft.Azure.WebJobs.Host
  • Microsoft.Azure.WebJobs.Extensions
  • System.Web.Http
  • System.Net.Http.Formatting

さらに、次のアセンブリは特別扱いされ、simplename によって参照される場合があります (例: #r "AssemblyName")。

  • Newtonsoft.Json
  • Microsoft.WindowsAzure.Storage
  • Microsoft.ServiceBus
  • Microsoft.AspNet.WebHooks.Receivers
  • Microsoft.AspNet.WebHooks.Common
  • Microsoft.Azure.NotificationHubs

プライベート アセンブリを参照する必要がある場合は、アセンブリ ファイルを関数に関連する bin フォルダーにアップロードし、ファイル名 (例: #r "MyAssembly.dll") を使用して参照できます。 関数フォルダーにファイルをアップロードする方法については、パッケージ管理の次のセクションを参照してください。

パッケージの管理

NuGet パッケージを C# 関数で使用するには、 project.json ファイルを関数アプリのファイル システムにある関数のフォルダーにアップロードします。 Microsoft.ProjectOxford.Face バージョン 1.1.0 への参照を追加する project.json ファイルの例を次に示します。

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.ProjectOxford.Face": "1.1.0"
      }
    }
   }
}

.NET Framework 4.6 のみがサポートされているので、次に示すように project.json ファイルが net46 を指定していることを確認します。

project.json ファイルをアップロードすると、ランタイムによってパッケージが取得され、パッケージ アセンブリに参照が自動的に追加されます。 #r "AssemblyName" ディレクティブを追加する必要はありません。 NuGet パッケージで定義されている型を使用するには、必要な using ステートメントを run.csx ファイルに追加するだけです。

Functions ランタイムでは、NuGet は project.jsonproject.lock.json を比較して作業を復元します。 ファイルの日付と時刻のタイムスタンプが一致しない場合は、NuGet 復元が実行され、NuGet は更新されたパッケージをダウンロードします。 ただし、ファイルの日付と時刻のタイムスタンプが一致した場合、NuGet は復元を実行しません。 したがって、これにより NuGet で復元がスキップされ、関数で必要なパッケージが取得されなくなるため、project.lock.json をデプロイすることはできません。 ロック ファイルのデプロイを回避するには、project.lock.json.gitignore ファイルに追加します。

Project.json ファイルをアップロードする方法

  1. Azure ポータルで関数を開き、関数アプリが実行中であることを確認して開始します。

    これにより、パッケージのインストール出力が表示されるストリーミング ログへのアクセス権が付与されます。

  2. project.json ファイルをアップロードするには、「 Azure Functions developer reference (Azure Functions 開発者向けリファレンス) 」の「 関数アプリ ファイルを更新する方法」セクションにあるいずれかの方法を利用してください。
  3. project.json ファイルがアップロードされた後、関数のストリーミング ログの出力は次の例のようになります。
2016-04-04T19:02:48.745 Restoring packages.
2016-04-04T19:02:48.745 Starting NuGet restore
2016-04-04T19:02:50.183 MSBuild auto-detection: using msbuild version '14.0' from 'D:\Program Files (x86)\MSBuild\14.0\bin'.
2016-04-04T19:02:50.261 Feeds used:
2016-04-04T19:02:50.261 C:\DWASFiles\Sites\facavalfunctest\LocalAppData\NuGet\Cache
2016-04-04T19:02:50.261 https://api.nuget.org/v3/index.json
2016-04-04T19:02:50.261 
2016-04-04T19:02:50.511 Restoring packages for D:\home\site\wwwroot\HttpTriggerCSharp1\Project.json...
2016-04-04T19:02:52.800 Installing Newtonsoft.Json 6.0.8.
2016-04-04T19:02:52.800 Installing Microsoft.ProjectOxford.Face 1.1.0.
2016-04-04T19:02:57.095 All packages are compatible with .NETFramework,Version=v4.6.
2016-04-04T19:02:57.189 
2016-04-04T19:02:57.189 
2016-04-04T19:02:57.455 Packages restored.

環境変数

環境変数またはアプリ設定値を取得するには、次のコード例のように、 System.Environment.GetEnvironmentVariableを使用します。

public static void Run(TimerInfo myTimer, TraceWriter log)
{
    log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
    log.Info(GetEnvironmentVariable("AzureWebJobsStorage"));
    log.Info(GetEnvironmentVariable("WEBSITE_SITE_NAME"));
}

public static string GetEnvironmentVariable(string name)
{
    return name + ": " + 
        System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
}

.csx コードの再利用

他の .csx ファイルで定義されたクラスとメソッドを、run.csx ファイルで使用できます。 そのためには、run.csx ファイル内で #load ディレクティブを使用します。 次の例では、MyLogger という名前のログ記録ルーチンが myLogger.csx 内で共有され、#load ディレクティブを使用して run.csx に読み込まれます。

run.csxの例:

#load "mylogger.csx"

public static void Run(TimerInfo myTimer, TraceWriter log)
{
    log.Verbose($"Log by run.csx: {DateTime.Now}"); 
    MyLogger(log, $"Log by MyLogger: {DateTime.Now}");
}

mylogger.csxの例:

public static void MyLogger(TraceWriter log, string logtext)
{
    log.Verbose(logtext); 
}

共有された .csx の使用は、POCO オブジェクトを使用して関数間の引数を厳密に型宣言する場合の一般的なパターンです。 次の簡略化された例では、HTTP トリガーとキュー トリガーが Order という名前の POCO オブジェクトを共有して、注文データを厳密に型宣言しています。

例: HTTP トリガーの run.csx

#load "..\shared\order.csx"

using System.Net;

public static async Task<HttpResponseMessage> Run(Order req, IAsyncCollector<Order> outputQueueItem, TraceWriter log)
{
    log.Info("C# HTTP trigger function received an order.");
    log.Info(req.ToString());
    log.Info("Submitting to processing queue.");

    if (req.orderId == null)
    {
        return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }
    else
    {
        await outputQueueItem.AddAsync(req);
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

例: キュー トリガーの run.csx

#load "..\shared\order.csx"

using System;

public static void Run(Order myQueueItem, out Order outputQueueItem,TraceWriter log)
{
    log.Info($"C# Queue trigger function processed order...");
    log.Info(myQueueItem.ToString());

    outputQueueItem = myQueueItem;
}

例: order.csx

public class Order
{
    public string orderId {get; set; }
    public string custName {get; set;}
    public string custAddress {get; set;}
    public string custEmail {get; set;}
    public string cartId {get; set; }

    public override String ToString()
    {
        return "\n{\n\torderId : " + orderId + 
                  "\n\tcustName : " + custName +             
                  "\n\tcustAddress : " + custAddress +             
                  "\n\tcustEmail : " + custEmail +             
                  "\n\tcartId : " + cartId + "\n}";             
    }
}

#load ディレクティブで相対パスを使用できます。

  • #load "mylogger.csx" によって、関数フォルダーにあるファイルが読み込まれます。
  • #load "loadedfiles\mylogger.csx" によって、関数フォルダー内のフォルダーにあるファイルが読み込まれます。
  • #load "..\shared\mylogger.csx" によって、関数フォルダーと同じレベル ( wwwrootの直下) にあるフォルダーのファイルが読み込まれます。

#load ディレクティブは、.csx (C# スクリプト) ファイルでのみ機能し、.cs ファイルでは機能しません。

バージョン管理

Functions ランタイムは Function App のサイト拡張として実行します。 サイト拡張は、Azure App Service、Web サイト、または Function App に機能を追加するための機能拡張ポイントです。 Kudu および Monaco はサイト拡張の 2 つの例で、カスタム拡張も作成および使用できます。 FUNCTIONS_EXTENSION_VERSION アプリ設定を使用して拡張のバージョンを構成できます。

FUNCTIONS_EXTENSION_VERSION は、ランタイムのメジャー バージョンのみを設定します。 たとえば、「~1」の値は、Function App がそのメジャー バージョンとして 1 を使用することを示します。 Function App は、リリースされたときにそれぞれの新しいマイナー バージョンにアップグレードされます。 これにより、バージョンをいつアップグレードするかを管理して、重大な変更を回避できます。

さらに、ランタイムがポータルの既定のバージョンになる前に、ランタイムをアップグレードすることがあります。 その場合は、FUNCTIONS_EXTENSION_VERSION の設定を古い値に戻すことでいつでもロールバックできます。

Azure Function App のランタイム バージョンを決定するには:

Kudu の D:\local\Config フォルダーにある applicationhost.config ファイルを見つけます。 virtualDirectory エントリには関数の正確なランタイム バージョンが表示されます。

<virtualDirectory path="/" physicalPath="D:\Program Files (x86)\SiteExtensions\Functions\0.8.10564" />

この値を使用して、Function App の特定のメジャーおよびマイナー ランタイム バージョンを設定できます。 Function App のバージョンを変更するたびに再起動する必要があります。

実行時の高度なバインド (命令型のバインド)

C# および他の .NET 言語では、function.json宣言型のバインドではなく命令型のバインド パターンを使用できます。 命令型のバインドは、設計時ではなくランタイム時にバインド パラメーターを計算する必要がある場合に便利です。 このパターンを使用すると、サポートされている任意の数の入力バインドと出力バインドに関数コード内でバインドできます。

次のように命令型のバインドを定義します。

  • 必要な命令型のバインドの function.json にエントリを含めないでください。
  • 入力パラメーター Binder binder または IBinder binder を渡します。
  • 次の C# パターンを使用してデータ バインドを実行します。
using (var output = await binder.BindAsync<T>(new BindingTypeAttribute(...)))
{
    ...
}

ここで、BindingTypeAttribute はバインドを定義する .NET 属性、T はそのバインドの種類でサポートされている入力または出力の型です。 Tout パラメーター型 (out JObject など) にすることはできません。 たとえば、Mobile Apps テーブルの出力バインドは 6 種類の出力をサポートしますが、T に使用できるのは ICollector または IAsyncCollector のみです。

次のコード例は、実行時に BLOB パスが定義された Storage Blob の出力バインドを作成し、この BLOB に文字列を書き込みます。

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings.Runtime;

public static async Task Run(string input, Binder binder)
{
    using (var writer = await binder.BindAsync<TextWriter>(new BlobAttribute("samples-output/path")))
    {
        writer.Write("Hello World!!");
    }
}

BlobAttributeStorage Blob の入力バインドまたは出力バインドを定義します。TextWriter はサポートされている出力バインドの種類です。 このコードは、ストレージ アカウントの接続文字列 (AzureWebJobsStorage) の既定のアプリケーション設定を取得します。 StorageAccountAttribute を追加し、属性の配列を BindAsync<T>() に渡すことで、使用するカスタムなアプリケーション設定を指定できます。 たとえば、次のように入力します。

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings.Runtime;

public static async Task Run(string input, Binder binder)
{
    var attributes = new Attribute[]
    {    
        new BlobAttribute("samples-output/path"),
        new StorageAccountAttribute("MyStorageAccount")
    };

    using (var writer = await binder.BindAsync<TextWriter>(attributes))
    {
        writer.Write("Hello World!");
    }
}

次の表に、各バインドの種類の .NET 属性と、それらが定義されているパッケージを示します。

バインド Attribute 参照の追加
Cosmos DB Microsoft.Azure.WebJobs.DocumentDBAttribute #r "Microsoft.Azure.WebJobs.Extensions.DocumentDB"
Event Hubs Microsoft.Azure.WebJobs.ServiceBus.EventHubAttributeMicrosoft.Azure.WebJobs.ServiceBusAccountAttribute #r "Microsoft.Azure.Jobs.ServiceBus"
Mobile Apps Microsoft.Azure.WebJobs.MobileTableAttribute #r "Microsoft.Azure.WebJobs.Extensions.MobileApps"
Notification Hubs Microsoft.Azure.WebJobs.NotificationHubAttribute #r "Microsoft.Azure.WebJobs.Extensions.NotificationHubs"
Service Bus Microsoft.Azure.WebJobs.ServiceBusAttributeMicrosoft.Azure.WebJobs.ServiceBusAccountAttribute #r "Microsoft.Azure.WebJobs.ServiceBus"
ストレージ キュー Microsoft.Azure.WebJobs.QueueAttributeMicrosoft.Azure.WebJobs.StorageAccountAttribute
Storage Blob Microsoft.Azure.WebJobs.BlobAttributeMicrosoft.Azure.WebJobs.StorageAccountAttribute
ストレージ テーブル Microsoft.Azure.WebJobs.TableAttributeMicrosoft.Azure.WebJobs.StorageAccountAttribute
Twilio Microsoft.Azure.WebJobs.TwilioSmsAttribute #r "Microsoft.Azure.WebJobs.Extensions.Twilio"

次のステップ

詳細については、次のリソースを参照してください。