البرنامج التعليمي: إنشاء تطبيق .NET Service Fabric

هذا البرنامج التعليمي هو الجزء الأول في سلسلة. في هذا البرنامج التعليمي، تعرف على كيفية إنشاء تطبيق Azure Service Fabric يحتوي على واجهة برمجة تطبيقات ويب أساسية ASP.NET وخدمة خلفية ذات حالة لتخزين بياناتك. عند الانتهاء، يكون لديك تطبيق تصويت يحتوي على واجهة ويب ASP.NET Core الأمامية التي تحفظ نتائج التصويت في خدمة خلفية ذات حالة في نظام المجموعة.

تتطلب سلسلة البرامج التعليمية هذه كمبيوتر مطور Windows. إذا كنت لا تريد إنشاء تطبيق التصويت يدوياً، فيمكن تنزيل كود المصدر للتطبيق المكتمل الذهاب مباشرة إلى تفقد نموذج تطبيق التصويت. يمكنك أيضا عرض معاينة فيديو لهذا البرنامج التعليمي.

رسم تخطيطي يوضح الواجهة الأمامية لواجهة برمجة تطبيقات AngularJS+ASP.NET المتصلة بخدمة خلفية ذات حالة في Service Fabric.

في هذا البرنامج التعليمي، تتعلم كيفية:

  • إنشاء خدمة API ويب الأساسية ASP.NET كخدمة موثوق بها ومناسبة
  • إنشاء خدمة تطبيق ويب أساسية ASP.NET كخدمة ويب عديمة الحالة
  • استخدام الوكيل العكسي للاتصال بالخدمة ذات الحالة

توضح لك سلسلة البرامج التعليمية كيفية:

المتطلبات الأساسية

قبل أن تبدأ هذا البرنامج التعليمي:

إنشاء خدمة ASP.NET Web API كخدمة موثوق بها

أولا، قم بإنشاء واجهة الويب الأمامية لتطبيق التصويت باستخدام ASP.NET Core. ASP.NET Core هو إطار عمل تطوير للويب عبر المنصات خفيف بحيث يمكنك استخدامه لإنشاء واجهة مستخدم ويب حديثة وواجهات برمجة التطبيقات على الويب.

للحصول على فهم كامل لكيفية تكامل ASP.NET Core مع Service Fabric، نوصي بشدة بمراجعة ASP.NET Core في Service Fabric Reliable Services. في الوقت الحالي، يمكنك متابعة هذا البرنامج التعليمي للبدء بسرعة. لمعرفة المزيد حول ASP.NET Core، راجع وثائق ASP.NET Core.

لإنشاء الخدمة:

  1. افتح Visual Studio باستخدام الخيار تشغيل كمسؤول .

  2. حدد ملف>مشروع جديد>لإنشاء مشروع جديد.

  3. في Create a new project، حدد Cloud>Service Fabric Application. حدد التالي.

    لقطة شاشة تعرض مربع حوار إنشاء مشروع جديد في Visual Studio.

  4. حدد عديم الحالة ASP.NET Core لنوع المشروع الجديد، وقم بتسمية الخدمة VotingWeb، ثم حدد إنشاء.

    لقطة شاشة تعرض اختيار خدمة ويب ASP.NET في جزء الخدمة الجديد.

  5. يعرض الجزء التالي مجموعة من قوالب مشروع ASP.NET Core. في هذا البرنامج التعليمي، حدد تطبيق ويب (Model-View-Controller)، ثم حدد موافق.

    لقطة شاشة توضح تحديد نوع المشروع ASP.NET.

    ينشئ Visual Studio تطبيقا ومشروع خدمة، ثم يعرضهما في Visual Studio مستكشف الحلول:

    لقطة شاشة تعرض مستكشف الحلول بعد إنشاء التطبيق باستخدام خدمة واجهة برمجة تطبيقات الويب الأساسية 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 وافتح الملف. يحتوي هذا الملف على طريقة العرض الخاصة بوحدة تحكم الصفحة الرئيسية. استبدل محتوياته بالتعليمات البرمجية التالية، ثم احفظ التغييرات.

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

إضافة وحدة تحكم لتحديد إجراءات التصويت. انقر بزر الماوس الأيمن فوق مجلد Controllers، ثم حدد Add>New item>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 في مشروع التصويت بحيث يفتح مستعرض ويب إلى المنفذ الصحيح عند تصحيح أخطاء التطبيق الخاص بك. في مستكشف الحلول، حدد مشروع التصويت، ثم قم بتحديث الخاصية Application URL إلى 8080.

نشر وتشغيل تطبيق التصويت محلياً

يمكنك الآن تشغيل تطبيق التصويت لتصحيح أخطائه. في Visual Studio، حدد F5 لنشر التطبيق إلى مجموعة Service Fabric المحلية في وضع التصحيح. يفشل التطبيق إذا لم تفتح Visual Studio مسبقا باستخدام الخيار تشغيل كمسؤول .

إشعار

في المرة الأولى التي تقوم فيها بتشغيل التطبيق ونشره محليا، ينشئ Visual Studio مجموعة Service Fabric محلية لاستخدامها لتصحيح الأخطاء. قد تستغرق عملية إنشاء نظام مجموعة بعض الوقت. يتم عرض حالة إنشاء نظام المجموعة في نافذة Visual Studio Output.

بعد نشر تطبيق التصويت إلى مجموعة Service Fabric المحلية، يفتح تطبيق الويب الخاص بك تلقائيا في علامة تبويب المتصفح. يبدو مشابها لهذا المثال:

لقطة شاشة تعرض الواجهة الأمامية للتطبيق في مستعرض.

لإيقاف تصحيح أخطاء التطبيق، ارجع إلى Visual Studio وحدد Shift+F5.

إضافة خدمة خلفية مناسبة إلى التطبيق

الآن بعد تشغيل خدمة واجهة برمجة تطبيقات الويب ASP.NET في التطبيق، أضف خدمة موثوقة ذات حالة لتخزين بعض البيانات في التطبيق.

يمكنك استخدام Service Fabric لتخزين بياناتك بشكل متسق وموثوق داخل خدمتك مباشرة باستخدام مجموعات موثوقة. المجموعات الموثوقة هي مجموعة من فئات المجموعات عالية التوفر والموثوقية المألوفة لأي شخص لديه خبرة في استخدام مجموعات C#‎.

لإنشاء خدمة تخزن قيمة عداد في مجموعة موثوقة:

  1. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق Services في مشروع تطبيق Voting وحدد Add>New Service Fabric Service.

  2. في مربع الحوار New Service Fabric Service ، حدد Stateful ASP.NET Core، وقم بتسمية الخدمة VotingData، ثم حدد OK.

    بعد إنشاء مشروع الخدمة الخاص بك، لديك خدمتان في التطبيق الخاص بك. بينما تستمر في إنشاء التطبيق الخاص بك، يمكنك إضافة المزيد من الخدمات بنفس الطريقة. يمكن إصدار كل خدمة وترقيتها بشكل مستقل.

  3. يعرض الجزء التالي مجموعة من قوالب مشروع ASP.NET Core. لهذا البرنامج التعليمي، حدد API.

    ينشئ Visual Studio مشروع خدمة VotingData ويعرضه في مستكشف الحلول:

    لقطة شاشة تعرض مشروع خدمة VotingData في مستكشف الحلول.

إضافة ملف voteDataController.cs

في مشروع VotingData، انقر بزر الماوس الأيمن فوق المجلد Controllers، ثم حدد Add>New item>Class. قم بتسمية الملف 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، أو عبر واجهة برمجة تطبيقات HTTP REST، أو عبر بروتوكول WebSocket. للحصول على خلفية حول الخيارات المتوفرة ومفاضلاتها، راجع التواصل مع الخدمات.

يستخدم هذا البرنامج التعليمي ASP.NET Core Web API والوكيل العكسي ل 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 الأمامية التي تخدم صفحة الويب وتعرض واجهات برمجة تطبيقات الويب للتواصل مع الخدمة الخلفية.
  • خدمة خلفية (VotingData): خدمة ويب ASP.NET Core تعرض واجهة برمجة التطبيقات لتخزين نتائج التصويت في قاموس موثوق به يستمر على القرص.

رسم تخطيطي يصور خدمات التطبيق.

عند التصويت في التطبيق يحدث ما يلي:

  1. يرسل ملف JavaScript طلب التصويت إلى واجهة برمجة تطبيقات الويب في خدمة الواجهة الأمامية للويب كطلب HTTP PUT.

  2. تستخدم خدمة واجهة ويب الأمامية وكيلاً لتحديد موقع طلب HTTP PUT وإعادة توجيهه إلى الخدمة الخلفية.

  3. تأخذ الخدمة الخلفية الطلب الوارد وتخزن النتيجة المحدثة في قاموس موثوق به. يتم نسخ القاموس نسخا متماثلا إلى عقد متعددة في نظام المجموعة ويستمر على القرص. يتم تخزين جميع بيانات التطبيق في نظام المجموعة، ولذا لا توجد حاجة إلى قاعدة بيانات.

تتبع الأخطاء في Visual Studio

عند تتبع أخطاء تطبيق في Visual Studio، يمكنك استخدام مجموعة تطوير Service Fabric محلية. يمكن ضبط تجربة تتبع الأخطاء في الخاصة بك في السيناريو.

في هذا التطبيق، قم بتخزين البيانات في الخدمة الخلفية باستخدام قاموس موثوق به. يقوم Visual Studio بإزالة التطبيق بشكل افتراضي عند إيقاف مصحح الأخطاء. تؤدي إزالة التطبيق، إلى حذف البيانات التي في الخدمة الخلفية أيضاً. للاحتفاظ بالبيانات بين جلسات تتبع الأخطاء، يمكنك تغيير وضع تتبع الأخطاء كخاصية في مشروع التصويت في Visual Studio.

لمعرفة ما يحدث في التعليمات البرمجية، أكمل الخطوات التالية:

  1. افتح ملف VotingWeb\VotesController.cs وقم بتعيين نقطة توقف في أسلوب واجهة برمجة تطبيقات Put الويب (السطر 72).

  2. افتح ملف VotingData\VoteDataController.cs وقم بتعيين نقطة توقف في أسلوب واجهة برمجة تطبيقات Put الويب هذا (السطر 54).

  3. حدد F5 لبدء تشغيل التطبيق في وضع التصحيح.

  4. عد إلى المستعرض وحدد خيار تصويت أو أضف خيار تصويت جديدا. لقد وصلت إلى نقطة التوقف الأولى في وحدة تحكم واجهة برمجة التطبيقات للواجهة الأمامية للويب.

    يرسل JavaScript في المستعرض طلبا إلى وحدة تحكم واجهة برمجة تطبيقات الويب في خدمة الواجهة الأمامية:

    لقطة شاشة تظهر إضافة خدمة الواجهة الأمامية للتصويت.

    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.

الخطوة التالية

تقدم إلى البرنامج التعليمي الآتي: