Руководство. Создание приложения .NET Service Fabric

Это руководство является частью одной из ряда. В этом руководстве описано, как создать приложение Azure Service Fabric с интерфейсом веб-API ASP.NET Core и серверной службой с отслеживанием состояния для хранения данных. По завершении у вас есть приложение для голосования с ASP.NET веб-интерфейсом Core, которое сохраняет результаты голосования в серверной службе с отслеживанием состояния в кластере.

Для работы с этим учебником требуется компьютер разработчика Windows. Если вы не хотите вручную создавать приложение для голосования, вы можете скачать исходный код для завершенного приложения и сразу перейти к описанию примера приложения для голосования. Вы также можете просмотреть видео пошаговые руководства .

Схема, показывающая интерфейс API AngularJS+ASP.NET, подключающийся к серверной службе с отслеживанием состояния в Service Fabric.

В этом руководстве описано следующее:

  • Создание службы веб-API ASP.NET Core как надежной службы с отслеживанием состояния
  • Создание службы веб-приложения ASP.NET Core как службы без отслеживания состояния
  • Использование обратного прокси-сервера для взаимодействия со службой с отслеживанием состояния

В серии учебников показано, как:

Необходимые компоненты

Перед началом работы с этим руководством выполните следующие действия:

Создание службы веб-API ASP.NET как надежной службы

Сначала создайте веб-интерфейс приложения для голосования с помощью ASP.NET Core. ASP.NET Core — это простое кросс-платформенное средство для разработки веб-страниц, позволяющее создавать современные пользовательские веб-интерфейсы и интерфейсы веб-API.

Чтобы получить полное представление о том, как ASP.NET Core интегрируется с Service Fabric, настоятельно рекомендуется просмотреть ASP.NET Core в Service Fabric Reliable Services. А пока можете воспользоваться этим руководством, чтобы быстро приступить к работе. Дополнительные сведения о ASP.NET Core см. в документации по ASP.NET Core.

Чтобы создать службу, выполните следующие действия.

  1. Откройте Visual Studio с помощью параметра "Запуск от имени администратора ".

  2. Выберите файл>нового>проекта , чтобы создать новый проект.

  3. В разделе "Создание проекта" выберите приложение Cloud>Service Fabric. Выберите Далее.

    Снимок экрана: диалоговое окно

  4. Выберите "Без отслеживания состояния" ASP.NET Core для нового типа проекта, назовите службу VotingWeb и нажмите кнопку "Создать".

    Снимок экрана: выбор веб-службы ASP.NET на новой панели служб.

  5. На следующей панели показан набор шаблонов проектов ASP.NET Core. В этом руководстве выберите веб-приложение (Model-View-Controller) и нажмите кнопку "ОК".

    Снимок экрана: выбор типа проекта ASP.NET.

    Visual Studio создает приложение и проект службы, а затем отображает их в Visual Studio Обозреватель решений:

    Снимок экрана: Обозреватель решений после создания приложения с помощью основной веб-службы веб-API ASP.NET.

Обновление файла site.js

Перейдите к wwwroot/js/site.js и откройте файл. Замените содержимое файла следующим javaScript, используемым представлениями "Главная", а затем сохраните изменения.

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

Перейдите в views/Home/Index.cshtml и откройте файл. Этот файл имеет представление, относящееся к контроллеру home. Замените его содержимое следующим кодом, а затем сохраните изменения.

@{
    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

Перейдите в views/Shared/_Layout.cshtml и откройте файл. Этот файл имеет макет по умолчанию для приложения ASP.NET. Замените его содержимое следующим кодом, а затем сохраните изменения.

<!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

Откройте файл VotingWeb.cs. Этот файл создает ASP.NET Core WebHost внутри службы без отслеживания состояния с помощью веб-сервера WebListener.

В начале файла добавьте директиву using System.Net.Http; .

Замените функцию CreateServiceInstanceListeners() следующим кодом, а затем сохраните изменения.

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(), а затем сохраните изменения. При опросе GetVotingDataServiceName возвращает имя службы.

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

Добавление файла VotesController.cs

Добавьте контроллер для определения действий голосования. Щелкните правой кнопкой мыши папку "Контроллеры", а затем выберите "Добавить>новый элемент>Visual C#>ASP.NET Core>Class". Присвойте файлу имя VotesController.cs и нажмите кнопку "Добавить".

Замените содержимое файла VotesController.cs следующим кодом, а затем сохраните изменения. Затем на этапе Обновление файла VotesController.cs этот файл будет изменен для чтения и записи данных голосования, полученных из серверной службы. А пока контроллер возвращает статические строковые данные в представление.

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);
        }
     }
}

Настройка порта прослушивания

Когда создается интерфейсная служба VotingWeb, Visual Studio случайным образом выбирает порт для ее прослушивания. Служба VotingWeb выступает в качестве внешнего интерфейса для этого приложения и принимает внешний трафик. В этом разделе описано, как привязать эту службу к фиксированному и известному порту. Манифест служб объявляет конечные точки службы.

В обозревателе решений откройте файл VotingWeb/PackageRoot/ServiceManifest.xml. Resources В разделе найдите Endpoint элемент и измените Port значение 8080на .

Для локального развертывания и запуска приложения порт прослушивания приложения должен быть открыт и доступен на компьютере.

<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>

Затем обновите Application URL значение свойства в проекте Voting, чтобы веб-браузер открыл правильный порт при отладке приложения. В Обозреватель решений выберите проект voting, а затем обновите Application URL свойство 8080на .

Локальное развертывание и запуск приложения Voting

Теперь вы можете запустить приложение для голосования для отладки. В Visual Studio выберите F5, чтобы развернуть приложение в локальном кластере Service Fabric в режиме отладки. Приложение завершается ошибкой, если вы ранее не открыли Visual Studio с помощью параметра запуска от имени администратора .

Примечание.

При первом запуске и развертывании приложения Visual Studio создает локальный кластер Service Fabric для отладки. Процесс создания кластера может занять некоторое время. Состояние создания кластера отображается в окне вывода Visual Studio.

После развертывания приложения voting в локальном кластере Service Fabric веб-приложение автоматически открывается на вкладке браузера. Похоже на этот пример:

Снимок экрана: интерфейс приложения в браузере.

Чтобы остановить отладку приложения, вернитесь в Visual Studio и выберите SHIFT+F5.

Добавление в приложение серверной службы с отслеживанием состояния

Теперь, когда служба веб-API ASP.NET запущена в приложении, добавьте надежную службу с отслеживанием состояния для хранения некоторых данных в приложении.

Service Fabric можно использовать для согласованного и надежного хранения данных прямо в службе с помощью надежных коллекций. Надежные коллекции — это набор высокодоступных и надежных классов коллекций, знакомых любому, у кого есть опыт использования коллекций C#.

Чтобы создать службу, которая хранит значение счетчика в надежной коллекции:

  1. В Обозреватель решений щелкните правой кнопкой мыши службы в проекте приложения "Голосование" и выберите "Добавить>новую службу Service Fabric".

  2. В диалоговом окне "Новая служба Service Fabric" выберите "С отслеживанием состояния" ASP.NET Core, назовите службу VotingData и нажмите кнопку "ОК".

    После создания проекта службы в приложении есть две службы. При продолжении сборки приложения можно добавить больше служб таким же образом. Каждая служба может быть независимо версии и обновлена.

  3. На следующей панели показан набор шаблонов проектов ASP.NET Core. В этом руководстве выберите API.

    Visual Studio создает проект службы VotingData и отображает его в Обозреватель решений:

    Снимок экрана: проект службы VotingData в Обозреватель решений.

Добавление файла VoteDataController.cs

В проекте VotingData щелкните правой кнопкой мыши папку Controllers и выберите "Добавить>новый класс элемента>". Назовите файл VoteDataController.cs и нажмите кнопку "Добавить". Замените содержимое файла следующим кодом, а затем сохраните изменения.

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();
                }
            }
        }
    }
}

Подключение служб

В этом разделе описано, как подключить две службы. Вы делаете интерфейсное веб-приложение получать информацию о голосовании из серверной службы, а затем задаете сведения в приложении.

Service Fabric обеспечивает полную гибкость в том, как вы взаимодействуете с надежными службами. В одном приложении могут быть службы, доступные через TCP/IP, через REST API HTTP или через протокол WebSocket. Общие сведения о доступных вариантах и их компромиссах см. в разделе "Взаимодействие с службами".

В этом руководстве используется веб-API ASP.NET Core и обратный прокси-сервер Service Fabric, чтобы интерфейсная веб-служба VotingWeb могли взаимодействовать со серверной службой VotingData. Обратный прокси-сервер настроен по умолчанию для использования порта 19081. Обратный прокси-порт устанавливается в шаблоне Azure Resource Manager, который настраивает кластер. Чтобы найти используемый порт, просмотрите шаблон кластера в ресурсе Microsoft.ServiceFabric/clusters :

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

Чтобы найти обратный прокси-порт, используемый в локальном кластере разработки, просмотрите HttpApplicationGatewayEndpoint элемент в манифесте локального кластера Service Fabric:

  1. Чтобы открыть средство Service Fabric Explorer, откройте браузер и перейдите к ней http://localhost:19080.
  2. Выберите манифест кластера>.
  3. Запишите HttpApplicationGatewayEndpoint порт элемента. По умолчанию порт равен 19081. Если это не 19081, измените порт в GetProxyAddress методе кода VotesController.cs , как описано в следующем разделе.

Обновление файла VotesController.cs

В проекте VotingWeb откройте файл Controllers/VotesController.cs . Замените содержимое VotesController определения класса следующим кодом, а затем сохраните изменения. Если обратный прокси-порт, обнаруженный на первиозном шаге, не является 19081, измените порт в GetProxyAddress методе на 19081 обнаруженный порт.

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';
    }
}

Описание примера приложения для голосования

Приложение для голосования состоит из двух служб:

  • Веб-интерфейсная служба (VotingWeb): служба веб-интерфейса ASP.NET Core, которая обслуживает веб-страницу и предоставляет веб-API для взаимодействия с серверной службой.
  • Серверная служба (VotingData): веб-служба ASP.NET Core, которая предоставляет API для хранения результатов голосования в надежном словаре, сохраняемом на диске.

Схема, изображающая службы приложений.

Во время голосования в приложении происходят следующие события.

  1. Файл JavaScript отправляет запрос на голосование веб-API в веб-интерфейсной службе в виде HTTP-запроса PUT.

  2. Служба веб-интерфейса использует прокси, чтобы обнаружить и перенаправить запрос HTTP PUT внутренней службе.

  3. Серверная служба принимает входящий запрос и сохраняет обновленный результат в надежном словаре. Словарь реплицируется на несколько узлов в кластере и сохраняется на диске. Все данные приложения хранятся в кластере, поэтому база данных не требуется.

Отладка в Visual Studio

При отладке приложения в Visual Studio используется локальный кластер разработки Service Fabric. Вы можете настроить отладку для своего сценария.

В этом приложении храните данные в серверной службе с помощью надежного словаря. Visual Studio удаляет приложение по умолчанию при остановке отладчика. При удалении приложения данные во внутренней службе также удаляются. Для сохранения данных между сеансами отладки можно изменить свойство Режим отладки приложения проекта Voting в Visual Studio.

Чтобы узнать, что происходит в коде, выполните следующие действия.

  1. Откройте файл VotingWeb\VotesController.cs и задайте точку останова в методе веб-API Put (строка 72).

  2. Откройте файл VotingData\VoteDataController.cs и задайте точку останова в методе веб-API Put (строка 54).

  3. Выберите F5, чтобы запустить приложение в режиме отладки.

  4. Вернитесь в браузер и выберите вариант голосования или добавьте новый вариант голосования. Вы попали в первую точку останова в контроллере API веб-интерфейса.

    JavaScript в браузере отправляет запрос контроллеру веб-API в интерфейсной службе:

    Снимок экрана: добавление интерфейсной службы голосования.

    1. Сначала создайте URL-адрес обратного прокси-сервера для серверной службы. (1)
    2. Затем отправьте HTTP-запрос PUT обратному прокси-серверу. (2)
    3. Наконец, верните ответ от серверной службы клиенту. (3)
  5. Выберите F5, чтобы продолжить.

    Теперь вы находитесь в точке останова в внутренней службе:

    Снимок экрана: добавление голосования в службу внутреннего сервера.

    1. В первой строке метода используйте stateManager для получения или добавления надежного словаря counts. (1)
    2. Для всех взаимодействий, имеющих значения в надежном словаре, требуется транзакция. Эта using инструкция создает эту транзакцию. (2)
    3. В транзакции обновите значение соответствующего ключа для параметра голосования и зафиксируйте операцию. При возврате commit метода данные обновляются в словаре. Затем он реплицируется на другие узлы в кластере. Теперь данные безопасно хранятся в кластере, а служба внутренней службы может выполнить отработку отказа на другие узлы и по-прежнему иметь доступные данные. (3)
  6. Выберите F5, чтобы продолжить.

Чтобы остановить сеанс отладки, выберите SHIFT+F5.

Следующий шаг

Перейдите к следующему руководству: