Как я могу вызвать асинхронный процесс из размещенного модуля asp.net, используя C # 5 async / await?

У меня есть длительный процесс, который вызывается с помощью метода Get модуля Nancy. Вместо того, чтобы браузер зависал во время работы процесса, я хотел бы, чтобы этот вызов был асинхронным, другими словами, я хочу, чтобы метод Get сразу возвращался и оставлял длительно работающий процесс на свое место. Затем я мог бы регулярно проверять состояние процесса и действовать соответствующим образом.

Я новичок в асинхронных / ожидающих функциях C # 5, но я уверен, что они предназначены именно для такого рода задач. Код ниже показывает мою попытку. Я вставил некоторые записи, которые демонстрируют, что он не работает асинхронно, как ожидалось. Вместо этого длительный процесс блокирует метод Get и браузер зависает.

Module

public class TestModule : NancyModule
{            
    public TestModule(ITestService testService)
    {
        Get["/longprocess"] = _ =>
            {
                Log.Write("Module : Start");
                testService.LongProcess();
                Log.Write("Module : Finish");
                return HttpStatusCode.OK;
            };
    }
}

Service

public interface ITestService
{
    Task<bool> LongProcess();
}

public class TestService : ITestService
{
    public async Task<bool> LongProcess()
    {
        await LongProcessAsynch();
        return true;
    }

    private Task<bool> LongProcessAsynch()
    {
        Log.Write("LongProcess : Start");
        Thread.Sleep(5000);
        //Task.Delay(5000); <- Has same effect
        Log.Write("LongProcess : Finish");
        return true.AsTask();
    }
}

Extension Method

public static Task<T> AsTask<T>(this T candidate)
{
    var source = new TaskCompletionSource<T>();
    source.SetResult(candidate);
    return source.Task;
}

Log Output

14/06/2012 19:22:29 : Module : Start
14/06/2012 19:22:29 : LongProcess : Start
14/06/2012 19:22:34 : LongProcess : Finish
14/06/2012 19:22:34 : Module : Finish

Вы можете видеть из выхода журнала выше, чтоLongProcess() блокирует возврат модуля Get. Если бы задача выполнялась асинхронно, я бы ожидал, что журнал будет выглядеть примерно так:

Expected Log

Module : Start
LongProcess : Start
Module : Finish
LongProcess : Finish

Я думаю, что на самом деле требуется поставитьawait в методе Get NancyModule. Возможно, что-то вроде приведенного ниже кода, но я не могу сделать это, потому что я не могу пометить конструктор модуля какasnyc и поэтому я не могу использоватьawait в методе Get (или так в настоящее время я верю)

Get["/longprocess"] = _ =>
    {
        Log.Write("Module : Start");
        await testService.LongProcess();
        Log.Write("Module : Finish");
        return HttpStatusCode.OK;
    }; 

Спасибо за любую помощь, примеры или указатели на ресурсы.

Edit 1

Дополнительные исследования показывают, что asp.net активно предотвращаетasync звонки по умолчанию. Что-то делать сSession и то, как он обрабатывает не потоки пользовательского интерфейса, я верю. Поэтому я добавил следующее в мой web.config (см. Ниже)

UseTaskFriendlySynchronizationContext enableSessionState="false"

К сожалению, это не имеет никакого значения, и мойasync / await позвонитьLongProcess() все еще блокирует

<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
    <add key="webPages:Enabled" value="false" />
  </appSettings>
  <system.web>
    <httpRuntime targetFramework="4.5"/>
    <pages controlRenderingCompatibilityVersion="4.0" enableSessionState="false" />
    ... more config
</configuration>
 usr14 июн. 2012 г., 23:08
Я не знаю, что такое NancyFX, но он должен поддерживать Задачи в качестве возвращаемых значений, чтобы вы могли использовать его асинхронно.
 biofractal15 июн. 2012 г., 10:23
Я вложил асинхронный вызов, чтобы попытаться обойти это. Первоначальный вызовtestService.LongProcess() синхронно, но это тогда вызываетLongProcessAsynch() метод с использованиемawait Ключевое слово и поэтому это должно быть асинхронным. Вы считаете это правильным?

Ответы на вопрос(2)

попробуйте установить атрибут страницыAsync="true"in the aspx page after doing that it wold look like this <%@ Page Language="C#" AutoEventWireup="true" CodeFile="XXX.aspx.cs" Inherits="xxx" Async="true" %>

в файле Tcode behile вы можете сделать задачи и методы типа async и calllawait  задачи от них ...

это может сработать

Решение Вопроса

e meant for just this kind of task.

Нет.async/await предназначены для облегчения асинхронного программированияwithin the context of a single process, Они не меняют природу HTTP-коммуникаций.

Когда вы используетеawait на сервере ASP.NET (в контексте HTTP-запроса) вы возвращаете поток ASP.NET в пул потоков. Однако ASP.NET знает, что HTTP-запрос не завершен, поэтому он не завершает ответ клиенту (браузеру).

Это по замыслу.

Вам нужно использовать AJAX, SignalR или другое решение, чтобы делать то, что вы хотите.

 biofractal20 июн. 2012 г., 14:37
В настоящее время я использую jQuery $ .ajax (), чтобы сделать мой асинхронный вызов для моего API. Конечно, это не совсем асинхронно, поскольку asp.net блокирует ответ, как вы описываете. Так что мой единственный вариант - раскрутить новый поток и вернуть идентификатор? Это, безусловно, сработает, но я хочу дважды проверить, что я не могу использовать новые функции асинхронной передачи, учитывая атрибут configUseTaskFriendlySynchronizationContext? Извините, что повторяю мой вопрос, но я хочу быть уверенным, прежде чем я уйду и займусь решением старой проблемы.
 biofractal21 июн. 2012 г., 12:44
Хорошо, я согласен с тем, что я лаю не на том дереве, поэтому внимательно посмотрю на то, чего я пытаюсь достичь, и посмотрим, можно ли это сделать с помощью более простого подхода. Спасибо за совет.
 21 июн. 2012 г., 01:05
Вы не хотите раскручивать новый поток (это почти всегда ошибка в ASP.NET; подумайте об утилизации). Сделайте шаг назад и изучите ваши требования. Вы хотите выполнить команду, которая запустит рабочий процесс. Веб-серверы не были предназначены для этого; вам действительно нужно как-то сохранить его (например, MSMQ / Azure Queues) или передать его другому серверу (например, службе Win32).

Ваш ответ на вопрос