チュートリアル:ASP.NET Core Web API フロントエンド サービスとステートフルなバックエンド サービスを含むアプリケーションを作成およびデプロイするTutorial: Create and deploy an application with an ASP.NET Core Web API front-end service and a stateful back-end service

このチュートリアルは、シリーズの第 1 部です。This tutorial is part one of a series. ASP.NET Core Web API フロント エンドとステートフルなバックエンド サービスを含む Azure Service Fabric アプリケーションを作成し、データを格納する方法を説明します。You will learn how to create an Azure Service Fabric application with an ASP.NET Core Web API front end and a stateful back-end service to store your data. 最後まで読み進めていけば、ASP.NET Core Web フロントエンドからクラスター内のステートフルなバックエンド サービスに投票結果を保存するアプリケーションが完成します。When you're finished, you have a voting application with an ASP.NET Core web front-end that saves voting results in a stateful back-end service in the cluster. 投票アプリケーションを手動で作成しない場合は、完成したアプリケーションのソース コードをダウンロードし、「投票のサンプル アプリケーションの概要」に進むことができます。If you don't want to manually create the voting application, you can download the source code for the completed application and skip ahead to Walk through the voting sample application. 必要に応じて、このチュートリアルのビデオ ウォークスルーをご覧になることもできます。If you prefer, you can also watch a video walk-through of this tutorial.

AngularJS+ASP.NET API フロントエンド、Service Fabric のステートフル バックエンド サービスに接続

シリーズの第 1 部で学習する内容は次のとおりです。In part one of the series, you learn how to:

  • ASP.NET Core Web API サービスをステートフル リライアブル サービスとして作成するCreate an ASP.NET Core Web API service as a stateful reliable service
  • ASP.NET Core Web アプリケーション サービスをステートレス Web サービスとして作成するCreate an ASP.NET Core Web Application service as a stateless web service
  • リバース プロキシを使用してステートフル サービスと通信するUse the reverse proxy to communicate with the stateful service

このチュートリアル シリーズで学習する内容は次のとおりです。In this tutorial series you learn how to:

前提条件Prerequisites

このチュートリアルを開始する前にBefore you begin this tutorial:

ASP.NET Web API サービスをリライアブル サービスとして作成するCreate an ASP.NET Web API service as a reliable service

まず、ASP.NET Core を使用して投票アプリケーション Web フロントエンドを作成します。First, create the web front-end of the voting application using ASP.NET Core. ASP.NET Core は軽量のクロスプラットフォーム Web 開発フレームワークであり、これを使用すると、最新の Web UI と Web API を作成できます。ASP.NET Core is a lightweight, cross-platform web development framework that you can use to create modern web UI and web APIs. Service Fabric と ASP.NET Core を統合する方法を十分に理解するには、「Service Fabric リライアブル サービスでの ASP.NET Core」の記事全体を読むことを強くお勧めします。To get a complete understanding of how ASP.NET Core integrates with Service Fabric, we strongly recommend reading through the ASP.NET Core in Service Fabric Reliable Services article. ここでは、このチュートリアルに従って、すぐに開始することができます。For now, you can follow this tutorial to get started quickly. ASP.NET Core の詳細については、ASP.NET Core のドキュメントを参照してください。To learn more about ASP.NET Core, see the ASP.NET Core Documentation.

  1. Visual Studio を管理者として起動します。Launch Visual Studio as an administrator.

  2. [ファイル] -> [新規] -> [プロジェクト] の順に選択してプロジェクトを作成します。Create a project with File->New->Project.

  3. [新しいプロジェクト] ダイアログで、 [クラウド]、[Service Fabric アプリケーション] の順に選択します。In the New Project dialog, choose Cloud > Service Fabric Application.

  4. アプリケーションに「Voting」という名前を付けて、 [OK] をクリックします。Name the application Voting and click OK.

    Visual Studio の [新しいプロジェクト] ダイアログ

  5. [新しい Service Fabric サービス] ページで、 [ステートレス ASP.NET Core] を選択し、サービスに「VotingWeb」 という名前を付けて、 [OK] をクリックします。On the New Service Fabric Service page, choose Stateless ASP.NET Core, name your service VotingWeb, then click OK.

    Choosing ASP.NET web service in the new service dialog

  6. 次のページには、一連の ASP.NET Core プロジェクト テンプレートが表示されます。The next page provides a set of ASP.NET Core project templates. このチュートリアルでは、 [Web アプリケーション (モデル ビュー コントローラー)] を選択し、 [OK] をクリックします。For this tutorial, choose Web Application (Model-View-Controller), then click OK.

    ASP.NET プロジェクトの種類を選択する

    Visual Studio によって、アプリケーションとサービス プロジェクトが作成され、ソリューション エクスプローラーに表示されます。Visual Studio creates an application and a service project and displays them in Solution Explorer.

    アプリケーションと ASP.NET Core Web API サービスの作成後に表示されるソリューション エクスプローラー

site.js ファイルを更新するUpdate the site.js file

wwwroot/js/site.js を開きます。Open wwwroot/js/site.js. その内容をホーム ビューによって使用される次の JavaScript に置き換えて、変更を保存します。Replace its contents with the following JavaScript used by the Home views, then save your changes.

var app = angular.module('VotingApp', ['ui.bootstrap']);
app.run(function () { });

app.controller('VotingAppController', ['$rootScope', '$scope', '$http', '$timeout', function ($rootScope, $scope, $http, $timeout) {

    $scope.refresh = function () {
        $http.get('api/Votes?c=' + new Date().getTime())
            .then(function (data, status) {
                $scope.votes = data;
            }, function (data, status) {
                $scope.votes = undefined;
            });
    };

    $scope.remove = function (item) {
        $http.delete('api/Votes/' + item)
            .then(function (data, status) {
                $scope.refresh();
            })
    };

    $scope.add = function (item) {
        var fd = new FormData();
        fd.append('item', item);
        $http.put('api/Votes/' + item, fd, {
            transformRequest: angular.identity,
            headers: { 'Content-Type': undefined }
        })
            .then(function (data, status) {
                $scope.refresh();
                $scope.item = undefined;
            })
    };
}]);

Index.cshtml ファイルを更新するUpdate the Index.cshtml file

ホーム コントローラーに固有のビューである Views/Home/Index.cshtml を開きます。Open Views/Home/Index.cshtml, the view specific to the Home controller. その内容を次のように置き換え、変更を保存します。Replace its contents with the following, then save your changes.

@{
    ViewData["Title"] = "Service Fabric Voting Sample";
}

<div ng-controller="VotingAppController" ng-init="refresh()">
    <div class="container-fluid">
        <div class="row">
            <div class="col-xs-8 col-xs-offset-2 text-center">
                <h2>Service Fabric Voting Sample</h2>
            </div>
        </div>

        <div class="row">
            <div class="col-xs-8 col-xs-offset-2">
                <form class="col-xs-12 center-block">
                    <div class="col-xs-6 form-group">
                        <input id="txtAdd" type="text" class="form-control" placeholder="Add voting option" ng-model="item"/>
                    </div>
                    <button id="btnAdd" class="btn btn-default" ng-click="add(item)">
                        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                        Add
                    </button>
                </form>
            </div>
        </div>

        <hr/>

        <div class="row">
            <div class="col-xs-8 col-xs-offset-2">
                <div class="row">
                    <div class="col-xs-4">
                        Click to vote
                    </div>
                </div>
                <div class="row top-buffer" ng-repeat="vote in votes.data">
                    <div class="col-xs-8">
                        <button class="btn btn-success text-left btn-block" ng-click="add(vote.key)">
                            <span class="pull-left">
                                {{vote.key}}
                            </span>
                            <span class="badge pull-right">
                                {{vote.value}} Votes
                            </span>
                        </button>
                    </div>
                    <div class="col-xs-4">
                        <button class="btn btn-danger pull-right btn-block" ng-click="remove(vote.key)">
                            <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
                            Remove
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

_Layout.cshtml ファイルを更新するUpdate the _Layout.cshtml file

ASP.NET アプリの既定のレイアウトである Views/Shared/_Layout.cshtml を開きます。Open Views/Shared/_Layout.cshtml, the default layout for the ASP.NET app. その内容を次のように置き換え、変更を保存します。Replace its contents with the following, then save your changes.

<!DOCTYPE html>
<html ng-app="VotingApp" xmlns:ng="https://angularjs.org">
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>@ViewData["Title"]</title>

    <link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet"/>
    <link href="~/css/site.css" rel="stylesheet"/>

</head>
<body>
<div class="container body-content">
    @RenderBody()
</div>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap-tpls.js"></script>
<script src="~/js/site.js"></script>

@RenderSection("Scripts", required: false)
</body>
</html>

VotingWeb.cs ファイルを更新するUpdate the VotingWeb.cs file

VotingWeb.cs ファイルを開きます。このファイルで、WebListener Web サーバーを使用してステートレス サービス内に ASP.NET Core WebHost が作成されます。Open the VotingWeb.cs file, which creates the ASP.NET Core WebHost inside the stateless service using the WebListener web server.

ファイルの先頭に using System.Net.Http; ディレクティブを追加します。Add the using System.Net.Http; directive to the top of the file.

CreateServiceInstanceListeners() 関数を次のコードに置き換えて、変更を保存します。Replace the CreateServiceInstanceListeners() function with the following code, then save your changes.

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(
            serviceContext =>
                new KestrelCommunicationListener(
                    serviceContext,
                    "ServiceEndpoint",
                    (url, listener) =>
                    {
                        ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

                        return new WebHostBuilder()
                            .UseKestrel()
                            .ConfigureServices(
                                services => services
                                    .AddSingleton<HttpClient>(new HttpClient())
                                    .AddSingleton<FabricClient>(new FabricClient())
                                    .AddSingleton<StatelessServiceContext>(serviceContext))
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseStartup<Startup>()
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                            .UseUrls(url)
                            .Build();
                    }))
    };
}

また、次の GetVotingDataServiceName メソッドを CreateServiceInstanceListeners() の下に追加し、変更を保存します。Also add the following GetVotingDataServiceName method below CreateServiceInstanceListeners(), then save your changes. GetVotingDataServiceName は、ポーリングされるとサービス名を返します。GetVotingDataServiceName returns the service name when polled.

internal static Uri GetVotingDataServiceName(ServiceContext context)
{
    return new Uri($"{context.CodePackageActivationContext.ApplicationName}/VotingData");
}

VotesController.cs ファイルを追加するAdd the VotesController.cs file

投票アクションを定義するコントローラーを追加します。Add a controller, which defines voting actions. Controllers フォルダーを右クリックし、 [追加] > [新しい項目] > [Visual C#] > [ASP.NET Core] > [クラス] の順に選択します。Right-click on the Controllers folder, then select Add->New item->Visual C#->ASP.NET Core->Class. ファイルに「VotesController.cs」と名前を付け、 [追加] をクリックします。Name the file VotesController.cs, then click Add.

VotesController.cs ファイルの内容を次のように置き換えて、変更を保存します。Replace the VotesController.cs file contents with the following, then save your changes. 後の「VotesController.cs ファイルを更新する」では、バックエンド サービスで投票データの読み取りと書き込みを行うように、このファイルを変更します。Later, in Update the VotesController.cs file, this file is modified to read and write voting data from the back-end service. ここでは、コントローラーから静的文字列データをビューに返します。For now, the controller returns static string data to the view.

namespace VotingWeb.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Fabric;
    using System.Fabric.Query;
    using System.Linq;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Text;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Newtonsoft.Json;

    [Produces("application/json")]
    [Route("api/Votes")]
    public class VotesController : Controller
    {
        private readonly HttpClient httpClient;

        public VotesController(HttpClient httpClient)
        {
            this.httpClient = httpClient;
        }

        // GET: api/Votes
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            List<KeyValuePair<string, int>> votes= new List<KeyValuePair<string, int>>();
            votes.Add(new KeyValuePair<string, int>("Pizza", 3));
            votes.Add(new KeyValuePair<string, int>("Ice cream", 4));

            return Json(votes);
        }
     }
}

リスニング ポートを構成するConfigure the listening port

VotingWeb フロントエンド サービスが作成されると、Visual Studio は、リッスンするサービスのポートをランダムに選択します。When the VotingWeb front-end service is created, Visual Studio randomly selects a port for the service to listen on. VotingWeb サービスは、このアプリケーションのフロント エンドとして機能して外部のトラフィックを受け入れるため、このサービスを固定のウェルノウン ポートにバインドしましょう。The VotingWeb service acts as the front-end for this application and accepts external traffic, so let's bind that service to a fixed and well-know port. サービス マニフェストは、サービス エンドポイントを宣言します。The service manifest declares the service endpoints.

ソリューション エクスプローラーで、VotingWeb/PackageRoot/ServiceManifest.xml を開きます。In Solution Explorer, open VotingWeb/PackageRoot/ServiceManifest.xml. Resources セクションで Endpoint 要素を探し、Port の値を「8080」に変更します。Find the Endpoint element in the Resources section and change the Port value to 8080. アプリケーションをローカルでデプロイして実行するには、アプリケーションのリスニング ポートをコンピューター上で開いて、使用できるようにする必要があります。To deploy and run the application locally, the application listening port must be open and available on your computer.

<Resources>
    <Endpoints>
      <!-- This endpoint is used by the communication listener to obtain the port on which to 
           listen. Please note that if your service is partitioned, this port is shared with 
           replicas of different partitions that are placed in your code. -->
      <Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="8080" />
    </Endpoints>
  </Resources>

また、Voting プロジェクトの Application URL プロパティ値を更新し、アプリケーションをデバッグするときに正しいポートに対して Web ブラウザーが開くようにします。Also update the Application URL property value in the Voting project so a web browser opens to the correct port when you debug your application. ソリューション エクスプローラーで、Voting プロジェクトを選択し、 [アプリケーション URL] プロパティを「8080」に更新します。In Solution Explorer, select the Voting project and update the Application URL property to 8080.

Voting アプリケーションをローカルにデプロイして実行するDeploy and run the Voting application locally

Voting アプリケーションを実行してデバッグできる状態になりました。You can now go ahead and run the Voting application for debugging. Visual Studio で、F5 キーを押してアプリケーションをローカルの Service Fabric クラスターにデバッグ モードでデプロイします。In Visual Studio, press F5 to deploy the application to your local Service Fabric cluster in debug mode. Visual Studio を管理者として開いていない場合、アプリケーションは失敗します。The application will fail if you didn't previously open Visual Studio as administrator.

注意

初めてアプリケーションをローカルに実行してデプロイすると、Visual Studio によってデバッグ用にローカル Service Fabric クラスターが作成されます。The first time you run and deploy the application locally, Visual Studio creates a local Service Fabric cluster for debugging. クラスターの作成には、しばらく時間がかかる場合があります。Cluster creation may take some time. Visual Studio の出力ウィンドウにクラスターの作成状態が表示されます。The cluster creation status is displayed in the Visual Studio output window.

Voting アプリケーションがローカル Service Fabric クラスターにデプロイされた後、次のように Web アプリがブラウザーのタブで自動的に開ます。After the Voting application has been deployed to your local Service Fabric cluster, your web app will open in a browser tab automatically and should look like this:

ASP.NET Core フロントエンド

アプリケーションのデバッグを停止するには、Visual Studio に戻って Shift + F5 キーを押します。To stop debugging the application, go back to Visual Studio and press Shift+F5.

アプリケーションにステートフルなバックエンド サービスを追加するAdd a stateful back-end service to your application

ASP.NET Web API サービスがアプリケーションで実行されるようになりました。次に、ステートフル リライアブル サービスを追加して、アプリケーションにデータを格納しましょう。Now that an ASP.NET Web API service is running in the application, go ahead and add a stateful reliable service to store some data in the application.

Service Fabric では、Reliable Collection によってデータがサービス内に一貫して確実に格納されます。Service Fabric allows you to consistently and reliably store your data right inside your service by using reliable collections. Reliable Collection は、C# コレクションを使用したことがあるユーザーには馴染みのある可用性が高く信頼できる一連のコレクション クラスです。Reliable collections are a set of highly available and reliable collection classes that are familiar to anyone who has used C# collections.

このチュートリアルでは、Reliable Collection にカウンター値を格納するサービスを作成します。In this tutorial, you create a service which stores a counter value in a reliable collection.

  1. ソリューション エクスプローラーで、Voting アプリケーション プロジェクトの [サービス] を右クリックし、 [追加] > [Service Fabric サービスの新規作成...] の順に選択します。In Solution Explorer, right-click Services within the Voting application project and choose Add -> New Service Fabric Service....

  2. [新しい Service Fabric サービス] ダイアログ ボックスで、 [ステートフル ASP.NET Core] を選択し、サービスに「VotingData」という名前を付けて、 [OK] をクリックします。In the New Service Fabric Service dialog, choose Stateful ASP.NET Core, name the service VotingData, then press OK.

    作成したサービス プロジェクトでは、2 つのサービスがアプリケーションに含まれます。Once your service project is created, you have two services in your application. アプリケーションのビルドを続けながら、同じ方法でさらにサービスを追加することができます。As you continue to build your application, you can add more services in the same way. それぞれを個別にバージョン管理およびアップグレードできます。Each can be independently versioned and upgraded.

  3. 次のページには、一連の ASP.NET Core プロジェクト テンプレートが表示されます。The next page provides a set of ASP.NET Core project templates. このチュートリアルでは、 [API] を選択します。For this tutorial, choose API.

    Visual Studio によって VotingData サービス プロジェクトが作成され、ソリューション エクスプローラーに表示されます。Visual Studio creates the VotingData service project and displays it in Solution Explorer.

    ソリューション エクスプローラー

VoteDataController.cs ファイルを追加するAdd the VoteDataController.cs file

VotingData プロジェクトで Controllers フォルダーを右クリックし、 [追加] > [新しい項目] > [クラス] の順に選択します。In the VotingData project, right-click on the Controllers folder, then select Add->New item->Class. ファイルに「VoteDataController.cs」と名前を付け、 [追加] をクリックします。Name the file VoteDataController.cs and click Add. ファイルの内容を次のように置き換え、変更を保存します。Replace the file contents with the following, then save your changes.

namespace VotingData.Controllers
{
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.ServiceFabric.Data;
    using Microsoft.ServiceFabric.Data.Collections;

    [Route("api/[controller]")]
    public class VoteDataController : Controller
    {
        private readonly IReliableStateManager stateManager;

        public VoteDataController(IReliableStateManager stateManager)
        {
            this.stateManager = stateManager;
        }

        // GET api/VoteData
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            CancellationToken ct = new CancellationToken();

            IReliableDictionary<string, int> votesDictionary = await this.stateManager.GetOrAddAsync<IReliableDictionary<string, int>>("counts");

            using (ITransaction tx = this.stateManager.CreateTransaction())
            {
                Microsoft.ServiceFabric.Data.IAsyncEnumerable<KeyValuePair<string, int>> list = await votesDictionary.CreateEnumerableAsync(tx);

                Microsoft.ServiceFabric.Data.IAsyncEnumerator<KeyValuePair<string, int>> enumerator = list.GetAsyncEnumerator();

                List<KeyValuePair<string, int>> result = new List<KeyValuePair<string, int>>();

                while (await enumerator.MoveNextAsync(ct))
                {
                    result.Add(enumerator.Current);
                }

                return this.Json(result);
            }
        }

        // PUT api/VoteData/name
        [HttpPut("{name}")]
        public async Task<IActionResult> Put(string name)
        {
            IReliableDictionary<string, int> votesDictionary = await this.stateManager.GetOrAddAsync<IReliableDictionary<string, int>>("counts");

            using (ITransaction tx = this.stateManager.CreateTransaction())
            {
                await votesDictionary.AddOrUpdateAsync(tx, name, 1, (key, oldvalue) => oldvalue + 1);
                await tx.CommitAsync();
            }

            return new OkResult();
        }

        // DELETE api/VoteData/name
        [HttpDelete("{name}")]
        public async Task<IActionResult> Delete(string name)
        {
            IReliableDictionary<string, int> votesDictionary = await this.stateManager.GetOrAddAsync<IReliableDictionary<string, int>>("counts");

            using (ITransaction tx = this.stateManager.CreateTransaction())
            {
                if (await votesDictionary.ContainsKeyAsync(tx, name))
                {
                    await votesDictionary.TryRemoveAsync(tx, name);
                    await tx.CommitAsync();
                    return new OkResult();
                }
                else
                {
                    return new NotFoundResult();
                }
            }
        }
    }
}

サービスへの接続Connect the services

次の手順では、2 つのサービスを接続し、フロントエンド Web アプリケーションでバックエンド サービスの投票情報を取得および設定できるようにします。In this next step, connect the two services and make the front-end Web application get and set voting information from the back-end service.

Service Fabric は、Reliable Services との通信方法において完全な柔軟性を提供します。Service Fabric provides complete flexibility in how you communicate with reliable services. 1 つのアプリケーション内に、TCP 経由でアクセス可能なサービスが含まれる場合があります。Within a single application, you might have services that are accessible via TCP. そのほかにも、HTTP REST API を使用してアクセスできるサービスや、Web ソケットを介してアクセスできるサービスが含まれる場合があります。Other services that might be accessible via an HTTP REST API and still other services could be accessible via web sockets. 使用可能なオプションとトレードオフについては、サービスとの通信に関する記事を参照してください。For background on the options available and the tradeoffs involved, see Communicating with services.

このチュートリアルでは、ASP.NET Core Web APIService Fabric リバース プロキシを使用して、VotingWeb フロントエンド Web サービスがバックエンドの VotingData サービスと通信できるようにします。This tutorial uses ASP.NET Core Web API and the Service Fabric reverse proxy so the VotingWeb front-end web service can communicate with the back-end VotingData service. リバース プロキシは、ポート 19081 を使用するように既定で構成され、このチュートリアルで動作するはずです。The reverse proxy is configured by default to use port 19081 and should work for this tutorial. リバース プロキシ ポートは、クラスターを設定するために使用される Azure Resource Manager テンプレートで設定されます。The reverse proxy port is set in the Azure Resource Manager template used to set up the cluster. どのポートが使用されているかを確認するには、 Microsoft.ServiceFabric/clustersリソースから、クラスター テンプレートをご覧ください:To find which port is used, look in the cluster template in the Microsoft.ServiceFabric/clusters resource:

"nodeTypes": [
          {
            ...
            "httpGatewayEndpointPort": "[variables('nt0fabricHttpGatewayPort')]",
            "isPrimary": true,
            "vmInstanceCount": "[parameters('nt0InstanceCount')]",
            "reverseProxyEndpointPort": "[parameters('SFReverseProxyPort')]"
          }
        ],

ローカル開発クラスターで使用されているリバース プロキシ ポートを確認するには、ローカル Service Fabric クラスター マニフェスト内の HttpApplicationGatewayEndpoint 要素を確認します。To find the reverse proxy port used in your local development cluster, view the HttpApplicationGatewayEndpoint element in the local Service Fabric cluster manifest:

  1. ブラウザー ウィンドウを開き、http://localhost:19080 に移動して Service Fabric Explorer ツールを開きます。Open a browser window and navigate to http://localhost:19080 to open the Service Fabric Explorer tool.
  2. [クラスター] -> [マニフェスト] を選択します。Select Cluster -> Manifest.
  3. HttpApplicationGatewayEndpoint 要素のポートをメモします。Make a note of the HttpApplicationGatewayEndpoint element port. 既定では、これは 19081 のはずです。By default this should be 19081. 19081 ではない場合は、次の VotesController.cs コードの GetProxyAddress メソッドで、ポートを変更する必要があります。If it is not 19081, you will need to change the port in the GetProxyAddress method of the following VotesController.cs code.

VotesController.cs ファイルを更新するUpdate the VotesController.cs file

VotingWeb プロジェクトで Controllers/VotesController.cs ファイルを開きます。In the VotingWeb project, open the Controllers/VotesController.cs file. VotesController クラス定義の内容を次のように置き換え、変更を保存します。Replace the VotesController class definition contents with the following, then save your changes. 前回の手順で確認したリバース プロキシ ポートが 19081 ではない場合は、GetProxyAddress メソッドで使用されているポートを、19081 から確認したポートに変更します。If the reverse proxy port you discovered in the pervious step is not 19081, change the port used in the GetProxyAddress method from 19081 to the port that you discovered.

public class VotesController : Controller
{
    private readonly HttpClient httpClient;
    private readonly FabricClient fabricClient;
    private readonly StatelessServiceContext serviceContext;

    public VotesController(HttpClient httpClient, StatelessServiceContext context, FabricClient fabricClient)
    {
        this.fabricClient = fabricClient;
        this.httpClient = httpClient;
        this.serviceContext = context;
    }

    // GET: api/Votes
    [HttpGet("")]
    public async Task<IActionResult> Get()
    {
        Uri serviceName = VotingWeb.GetVotingDataServiceName(this.serviceContext);
        Uri proxyAddress = this.GetProxyAddress(serviceName);

        ServicePartitionList partitions = await this.fabricClient.QueryManager.GetPartitionListAsync(serviceName);

        List<KeyValuePair<string, int>> result = new List<KeyValuePair<string, int>>();

        foreach (Partition partition in partitions)
        {
            string proxyUrl =
                $"{proxyAddress}/api/VoteData?PartitionKey={((Int64RangePartitionInformation) partition.PartitionInformation).LowKey}&PartitionKind=Int64Range";

            using (HttpResponseMessage response = await this.httpClient.GetAsync(proxyUrl))
            {
                if (response.StatusCode != System.Net.HttpStatusCode.OK)
                {
                    continue;
                }

                result.AddRange(JsonConvert.DeserializeObject<List<KeyValuePair<string, int>>>(await response.Content.ReadAsStringAsync()));
            }
        }

        return this.Json(result);
    }

    // PUT: api/Votes/name
    [HttpPut("{name}")]
    public async Task<IActionResult> Put(string name)
    {
        Uri serviceName = VotingWeb.GetVotingDataServiceName(this.serviceContext);
        Uri proxyAddress = this.GetProxyAddress(serviceName);
        long partitionKey = this.GetPartitionKey(name);
        string proxyUrl = $"{proxyAddress}/api/VoteData/{name}?PartitionKey={partitionKey}&PartitionKind=Int64Range";

        StringContent putContent = new StringContent($"{{ 'name' : '{name}' }}", Encoding.UTF8, "application/json");
        putContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

        using (HttpResponseMessage response = await this.httpClient.PutAsync(proxyUrl, putContent))
        {
            return new ContentResult()
            {
                StatusCode = (int) response.StatusCode,
                Content = await response.Content.ReadAsStringAsync()
            };
        }
    }

    // DELETE: api/Votes/name
    [HttpDelete("{name}")]
    public async Task<IActionResult> Delete(string name)
    {
        Uri serviceName = VotingWeb.GetVotingDataServiceName(this.serviceContext);
        Uri proxyAddress = this.GetProxyAddress(serviceName);
        long partitionKey = this.GetPartitionKey(name);
        string proxyUrl = $"{proxyAddress}/api/VoteData/{name}?PartitionKey={partitionKey}&PartitionKind=Int64Range";

        using (HttpResponseMessage response = await this.httpClient.DeleteAsync(proxyUrl))
        {
            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                return this.StatusCode((int) response.StatusCode);
            }
        }

        return new OkResult();
    }


    /// <summary>
    /// Constructs a reverse proxy URL for a given service.
    /// Example: http://localhost:19081/VotingApplication/VotingData/
    /// </summary>
    /// <param name="serviceName"></param>
    /// <returns></returns>
    private Uri GetProxyAddress(Uri serviceName)
    {
        return new Uri($"http://localhost:19081{serviceName.AbsolutePath}");
    }

    /// <summary>
    /// Creates a partition key from the given name.
    /// Uses the zero-based numeric position in the alphabet of the first letter of the name (0-25).
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private long GetPartitionKey(string name)
    {
        return Char.ToUpper(name.First()) - 'A';
    }
}

投票のサンプル アプリケーションの概要Walk through the voting sample application

この投票アプリケーションは次の 2 つのサービスから成ります。The voting application consists of two services:

  • Web フロントエンド サービス (VotingWeb) - ASP.NET Core Web フロントエンド サービス。Web ページを表示すると共に、バックエンド サービスとやり取りするための Web API を公開します。Web front-end service (VotingWeb)- An ASP.NET Core web front-end service, which serves the web page and exposes web APIs to communicate with the backend service.
  • バックエンド サービス (VotingData) - ASP.NET Core Web サービス。ディスク上に永続化された信頼性の高いディクショナリに投票結果を保存する API を公開します。Back-end service (VotingData)- An ASP.NET Core web service, which exposes an API to store the vote results in a reliable dictionary persisted on disk.

アプリケーション ダイアグラム

アプリケーションで票を投じると、次のイベントが発生します。When you vote in the application the following events occur:

  1. JavaScript が Web フロントエンド サービスの Web API に HTTP PUT 要求として投票要求を送信します。A JavaScript sends the vote request to the web API in the web front-end service as an HTTP PUT request.

  2. Web フロントエンド サービスがプロキシを使用して HTTP PUT 要求を検出し、バックエンド サービスに転送します。The web front-end service uses a proxy to locate and forward an HTTP PUT request to the back-end service.

  3. バックエンド サービスが受信要求を受け取り、更新された結果を信頼性の高いディクショナリに保存すると、それがクラスター内の複数のノードにレプリケートされてディスク上に永続化されます。The back-end service takes the incoming request, and stores the updated result in a reliable dictionary, which gets replicated to multiple nodes within the cluster and persisted on disk. アプリケーションのデータはすべてクラスターに保存されるため、データベースは必要ありません。All the application's data is stored in the cluster, so no database is needed.

Visual Studio でのデバッグDebug in Visual Studio

Visual Studio でアプリケーションをデバッグするときは、ローカルの Service Fabric 開発クラスターを使用します。When debugging application in Visual Studio, you are using a local Service Fabric development cluster. デバッグのエクスペリエンスは実際のシナリオに合わせて調整することができます。You have the option to adjust your debugging experience to your scenario. このアプリケーションでは、信頼性の高いディクショナリを使ってバックエンド サービスにデータを保存します。In this application, store data in the back-end service using a reliable dictionary. 既定では、デバッガーを停止すると、Visual Studio によってアプリケーションが削除されます。Visual Studio removes the application per default when you stop the debugger. アプリケーションが削除されると、バックエンド サービス内のデータも削除されます。Removing the application causes the data in the back-end service to also be removed. デバッグ セッションの終了後もデータを維持するには、Visual Studio の Voting プロジェクトのプロパティで、 [アプリケーション デバッグ モード] を変更してください。To persist the data between debugging sessions, you can change the Application Debug Mode as a property on the Voting project in Visual Studio.

コードでどのような処理が実行されているのかを確認するには、次の手順に従います。To look at what happens in the code, complete the following steps:

  1. VotingWeb\VotesController.cs ファイルを開き、Web API の Put メソッド (72 行目) にブレークポイントを設定します。Open the VotingWeb\VotesController.cs file and set a breakpoint in the web API's Put method (line 72).

  2. VotingData\VoteDataController.cs ファイルを開き、この Web API の Put メソッド (54 行目) にブレークポイントを設定します。Open the VotingData\VoteDataController.cs file and set a breakpoint in this web API's Put method (line 54).

  3. F5 キーを押して、デバッグ モードでアプリケーションを開始します。Press F5 to start the application in debug mode.

  4. ブラウザーに戻り、投票の選択肢をクリックするか、新しい選択肢を追加します。Go back to the browser and click a voting option or add a new voting option. Web フロントエンドの API コントローラーで 1 つ目のブレークポイントに到達します。You hit the first breakpoint in the web front-end's api controller.

    1. ここは、JavaScript がブラウザーからフロントエンド サービスの Web API コントローラーに要求を送信する部分です。This is where the JavaScript in the browser sends a request to the web API controller in the front-end service.

      投票フロントエンド サービスの追加

    2. 最初に、バックエンド サービスの ReverseProxy の URL を構築します (1)First construct the URL to the ReverseProxy for the back-end service (1).

    3. 次に、HTTP PUT 要求を ReverseProxy に送信します (2)Then send the HTTP PUT Request to the ReverseProxy (2).

    4. 最後に、バックエンド サービスからの応答をクライアントに返します (3)Finally the return the response from the back-end service to the client (3).

  5. F5 キーを押して続行します。Press F5 to continue.

    1. 今度は、バックエンド サービスのブレークポイントに到達します。You are now at the break point in the back-end service.

      投票バックエンド サービスの追加

    2. メソッド (1) の先頭行では、StateManager を使用して信頼性の高いディクショナリ (counts) を取得または追加します。In the first line in the method (1) use the StateManager to get or add a reliable dictionary called counts.

    3. 信頼性の高いディクショナリ内の値とのすべてのやり取りにはトランザクションが必要です。この using ステートメント (2) によってトランザクションが作成されます。All interactions with values in a reliable dictionary require a transaction, this using statement (2) creates that transaction.

    4. トランザクションで、投票の選択肢に関連したキーの値を更新し、操作をコミットします (3)In the transaction, update the value of the relevant key for the voting option and commits the operation (3). コミット メソッドから制御が戻ると、ディクショナリ内のデータが更新され、クラスター内の他のノードにレプリケートされます。Once the commit method returns, the data is updated in the dictionary and replicated to other nodes in the cluster. これでデータが安全にクラスターに保存され、バックエンド サービスは、データの可用性を維持したまま他のノードにフェールオーバーすることができます。The data is now safely stored in the cluster, and the back-end service can fail over to other nodes, still having the data available.

  6. F5 キーを押して続行します。Press F5 to continue.

デバッグ セッションを停止するには、Shift + F5 キーを押します。To stop the debugging session, press Shift+F5.

次の手順Next steps

チュートリアルのこの部分で学習した内容は次のとおりです。In this part of the tutorial, you learned how to:

  • ASP.NET Core Web API サービスをステートフル リライアブル サービスとして作成するCreate an ASP.NET Core Web API service as a stateful reliable service
  • ASP.NET Core Web アプリケーション サービスをステートレス Web サービスとして作成するCreate an ASP.NET Core Web Application service as a stateless web service
  • リバース プロキシを使用してステートフル サービスと通信するUse the reverse proxy to communicate with the stateful service

次のチュートリアルに進みます。Advance to the next tutorial: