Подтвердить что ты не робот

Нужен многостраничный процесс ASP.NET MVC с обратной связью с пользователем

Я пытаюсь создать контроллер в своем проекте для доставки того, что может оказаться довольно сложным. В результате они могут занять относительно много времени, и индикатор прогресса, безусловно, поможет пользователям узнать, что все идет вперед. Отчет будет запущен через запрос AJAX, и идея состоит в том, что периодические запросы JSON получат статус и обновят индикатор выполнения.

Я экспериментировал с AsyncController, так как кажется, что это хороший способ запуска длинных процессов без привязки ресурсов, но он, похоже, не дает мне возможности проверить прогресс (и, похоже, блокирует дальнейшие JSON, и я пока не обнаружил, почему). После этого я пробовал прибегать к сохранению прогресса в статической переменной на контроллере и чтении статуса из этого - но, честно говоря, все кажется немного взломанным!

Все предложения с благодарностью приняты!

4b9b3361

Ответ 1

В этом примере я написал, что вы можете попробовать:

Контроллер:

public class HomeController : AsyncController
{
    public ActionResult Index()
    {
        return View();
    }

    public void SomeTaskAsync(int id)
    {
        AsyncManager.OutstandingOperations.Increment();
        Task.Factory.StartNew(taskId =>
        {
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(200);
                HttpContext.Application["task" + taskId] = i;
            }
            var result = "result";
            AsyncManager.OutstandingOperations.Decrement();
            AsyncManager.Parameters["result"] = result;
            return result;
        }, id);
    }

    public ActionResult SomeTaskCompleted(string result)
    {
        return Content(result, "text/plain");
    }

    public ActionResult SomeTaskProgress(int id)
    {
        return Json(new
        {
            Progress = HttpContext.Application["task" + id]
        }, JsonRequestBehavior.AllowGet);
    }
}

Вид:

<script type="text/javascript">
$(function () {
    var taskId = 543;
    $.get('/home/sometask', { id: taskId }, function (result) {
        window.clearInterval(intervalId);
        $('#result').html(result);
    });

    var intervalId = window.setInterval(function () {
        $.getJSON('/home/sometaskprogress', { id: taskId }, function (json) {
            $('#progress').html(json.Progress + '%');
        });
    }, 5000);
});
</script>

<div id="progress"></div>
<div id="result"></div>

Идея состоит в том, чтобы запустить асинхронную операцию, которая сообщит о ходе с использованием HttpContext.Application, что означает, что каждая задача должна иметь уникальный идентификатор. Затем на стороне клиента мы запускаем задачу, а затем отправляем несколько запросов AJAX (каждые 5 секунд) для обновления прогресса. Вы можете настроить параметры, чтобы приспособиться к вашему сценарию. Дальнейшее улучшение будет заключаться в добавлении обработки исключений.

Ответ 2

Через 4,5 года после ответа на этот вопрос у нас есть библиотека, которая может облегчить эту задачу: SignalR. Нет необходимости использовать разделяемое состояние (что плохо, потому что это может привести к неожиданным результатам), просто используйте класс HubContext для подключения к концентратору, который отправляет сообщения клиенту.

Сначала мы установили соединение SignalR, как обычно (см., например, здесь), за исключением того, что нам не нужен какой-либо серверный метод на нашем Хабе. Затем мы вызываем вызов AJAX нашей конечной точке/контроллеру/независимо и передаем идентификатор соединения, который мы получаем как обычно: var connectionId = $.connection.hub.id;. На стороне сервера вы можете начать свой процесс в другом потоке и перенаправить 200 OK клиенту. Процесс будет знать connectionId, чтобы он мог отправлять сообщения обратно клиенту, например:

GlobalHost.ConnectionManager.GetHubContext<LogHub>()
                              .Clients.Client(connectionId)
                              .log(message);

Здесь log - это метод на стороне клиента, который вы хотите вызвать с сервера, поэтому его следует определить так, как вы обычно делаете с SignalR:

$.connection.logHub.client.log = function(message){...};

Подробнее в своем блоге здесь

Ответ 3

Если у вас есть доступ к вашей машине веб-сервера в качестве выбора, вы можете создать службу Windows или простое консольное приложение для выполнения долговременной работы. Ваше веб-приложение должно добавить запись в db, чтобы указать, что операция должна начинаться, и ваша служба Windows, которая периодически проверяет наличие новых записей в db, должна начать выполнять задачу и сообщить о прогрессе в db. Ваше веб-приложение может использовать запрос ajax для проверки прогресса и показа пользователям.

Я использовал этот метод для реализации отчетов excel для приложения asp.net mvc. Этот отчет был создан службой Windows, которая работает на машине и постоянно проверяет наличие новых записей в таблице отчетов, и когда она находит новую запись, она начинает создавать отчет и указывать прогресс, обновляя поле записи. Приложение Asp.net mvc просто добавляет новую отчетную запись и отслеживает прогресс в базе данных до ее завершения, а затем предоставляет ссылку на готовый файл для загрузки.