Как последовательно запускать код C# и отображать некоторые данные в поле зрения, пока программа еще запущена

#c# #asp.net-mvc

Вопрос:

У меня есть запрос, который я выполняю в mvc5. Мне нужно записать время начала на экране, затем написать что-то вроде загрузки… затем записать время, когда работа завершена. Я не уверен, что использовать, чтобы написать это последовательно не все сразу, сейчас это мой код, который пишет все сразу после завершения работы.

    public IActionResult GetQuery()
    {
        ViewData["Timefrom"] = DateTime.Now;
        
        var Query= _context.Order.FromSqlRaw("EXECUTE [dbo].[GetStatusReport].ToList();
        
        ViewData["Timefrom2"] = DateTime.Now;
        
        return View(Query);
    }
 

и это мое мнение:

  @model IEnumerable<Order>
 @{
   ViewData["Title"] = "Home Page";
 }


<tbody>
Start Time: @ViewData["Timefrom"]   //need this showup 1st before query running 
@foreach (var item in Model)        //show loading when here  
{
    <tr>
        <td width="30%">@item.OrderId</td>
        <td width="30%">@item.Name</td>
        <td width="30%">@item.Location</td>
    </tr>
}  
End Time: @ViewData["Timefrom2"]    //show this at end when done running query
 

Комментарии:

1. Если вам нужно обновить состояние страницы, получить данные с сервера и снова обновить состояние страницы без перезагрузки страницы, то технология, которую вы ищете, — это «AJAX».

2. Ajax был бы плохим выбором, потому что он упомянул, что это более длительный процесс. Это означает, что ему придется выполнить запрос БД для вызова ajax, чтобы выяснить, сколько было обработано. Каждый вызов ajax приведет к дополнительной нагрузке и задержке процесса.

Ответ №1:

Ваш случай-идеальный кандидат для технологии под названием WebSocket. В Asp.net MVC вы можете использовать библиотеку SignalR для реализации веб-пакетов с очень небольшим количеством строк кода.

Обновление 15/10/2021: Вы можете следовать примеру этого ответа, чтобы узнать о технологии WebSocket и о том, как вы можете интегрировать ее в существующую Asp.net применение.

Например, вот как будет работать ваша страница:

  • Страница загружается из действия контроллера. Когда страница загружается, вы начинаете тяжелый процесс
  • После загрузки страницы она подключается к определенной конечной точке (с помощью WebSocket) на сервере. Используя соединение WebSocket, сервер и страница смогут отправлять/получать данные по мере необходимости — Двунаправленно! В этом случае сервер будет отправлять обновления на страницу при необходимости. Страница получит данные. И не нужно периодически извлекать данные с помощью Ajax.
  • В массовом процессе (который вы запустили, когда страница загружалась) вы обрабатываете данные и отправляете регулярное обновление в WebSocket
  • Любой клиент, подключенный к сокету, получит данные (в этом случае ваша страница является одним клиентом). Вы можете использовать одни и те же данные с разных страниц.

Вот схема того, как работает веб-сайт на основе SignalR (изображение собрано здесь, где вы получите подробное объяснение):

введите описание изображения здесь

Вот пример концентратора:

 using AutoMapper;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hubs
{
    public class ProcessHub : Hub
    {
        private static IHubContext<ProcessHub> _hubContext;
        public static Dictionary<int, List<KeyValuePair<string, string>>> AppClientStorage = new Dictionary<int, List<KeyValuePair<string, string>>>();

        public ProcessHub(IMapper mapper, IHubContext<ProcessHub> hubContext)
        {
            _hubContext = hubContext;
        }

        public async Task<string> RegisterToSocket(int userId)
        {
            KeyValuePair<string, string> keyValuePair = new KeyValuePair<string, string>(subscriptionTypeName, Context.ConnectionId);

            AddToDictionary(userId, keyValuePair);

            return "Successfully Registered.";
        }

        public static async Task NotifyUpdatesRingAsync(int userId)
        {
            if (AppClientStorage.ContainsKey(userId))
            {
                List<KeyValuePair<string, string>> userSubscriptions = AppClientStorage[userId];

                if (userSubscriptions != null)
                {
                    List<KeyValuePair<string, string>> subscribers = userSubscriptions.ToList();

                    foreach (var subsc in subscribers)
                    {
                        string data = "10% Processed. Please wait..."
                        await _hubContext.Clients.Client(subsc.Value).SendAsync("ReceiveProcessData", data);
                    }
                }
            }
        }

        private void AddToDictionary(int userId, KeyValuePair<string, string> keyValuePair)
        {
            if (!AppClientStorage.ContainsKey(userId))
            {
                AppClientStorage.Add(userId, new List<KeyValuePair<string, string>>() { keyValuePair });
            }
            else
            {
                List<KeyValuePair<string, string>> subscriptions = AppClientStorage[userId];
                List<KeyValuePair<string, string>> connectionList = subscriptions.Where(s => s.Key.Equals(keyValuePair.Key)).ToList();

                if (!connectionList.Any(s => s.Value.Equals(keyValuePair.Value)))
                {
                    subscriptions.Add(keyValuePair);
                    AppClientStorage[userId] = subscriptions;
                }
            }
        }
        
        public override Task OnDisconnectedAsync(Exception exception)
        {
            string connectionId = Context.ConnectionId;
            return base.OnDisconnectedAsync(exception);
        }       
    }
}
 

Затем в CShtml — коде для подключения к концентратору при загрузке страницы:

 <script type="text/javascript">

    var connection = new signalR.HubConnectionBuilder()
        .withUrl("/processhub")
        .withAutomaticReconnect()
        .build();

    connection.on("ReceiveProcessData", function (data) {
        // Data sent by the server from the NotifyUpdatesProcessAsync function will arrive here
        // Display the 'Data' on the page as needed
    });

    connection.start();

    function subscribe() {
        connection.invoke("RegisterToSocket", 123).then(function (data) {
            // 123 is ur user Id            
            //Display the data as needed
        }).catch(err => console.error(err.toString()));
    }

</script>
 

В действии/службе, в которой вы обрабатываете данные, просто вызовите метод концентратора, чтобы отправить данные клиенту следующим образом:

 await ProcessHub.NotifyUpdatesProcessAsync(123);
 

Здесь 123 — идентификатор пользователя, который получит данные.

Если вы не знаете, как настроить концентратор и подключить его при запуске, вы можете следовать любому учебнику в Интернете. Например, вот один из них.

Комментарии:

1. Будьте осторожны при использовании изображений других людей без указания их авторства. Используемое изображение, по-видимому, взято из pubnub.com/blog/what-are-websockets и, похоже, не лицензирован для повторного использования. Действительно, у них есть авторское право на их сайт: © 2010 — 2021 PubNub Inc. Все права защищены.

2. @HereticMonkey Спасибо, что указали на это. Я обновил ответ

3. Кроме того, хотя это может быть правдой, что пользователь может использовать веб-сокеты (и, в частности, SignalR) для решения этой проблемы, этот ответ не показывает им, как это сделать; он дает им общий обзор того, как работает технология, но не содержит кода, специфичного для вопроса. Это был бы отличный ответ на вопрос «Как я могу обновить страницу со стороны сервера, не выполняя полную обратную передачу в ASP.NET MVC» без конкретики, но в этом есть конкретика.

4. @HereticMonkey Что вы подразумеваете под «отсутствием кода, специфичного для данного вопроса»? Есть примеры кодов, которые любой может скопировать отсюда и использовать в своих приложениях.

5. Ну, а какое AddToDictionary отношение это имеет к функции операции GetQuery ? Как вывод NotifyUpdatesProcessAsync относится к представлению, которое OP показывает в своем коде?