How do I call an asynchronous process from an asp.net hosted module using C#5 async / await?

萝らか妹 提交于 2019-12-01 13:44:29

问题


I have a long running process that is called via a Nancy Module Get method. Rather than have the browser hang while the process runs I would like this call to be asynchronous, in other words I want the Get method to return right away and leave the long running process to do its thing. I could then check the status of the process at regular intervals and act accordingly.

I am new to the async / await features of C#5 but I feel sure these are meant for just this kind of task. The code below shows my attempt. I have put in some Logging that demonstrates that it is not running asynchronously as expected. Instead the long running process blocks the Get method and so the browser hangs.

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

You can see from the Log Output above that the LongProcess() is blocking the return of the Get module. If the task was running asynchronously I would expect the log to look something like this:

Expected Log

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

I think what is actually required is to put the await in the NancyModule Get method. Something like the code below perhaps, but I cannot do this because I cannot mark the module constructor as asnyc and so I cannot use await within the Get method (or so I currently believe)

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

Thanks for any help, examples or pointers to resources.

Edit 1

More research reveals that asp.net seems to actively prevent async calls by default. Something to do with the Session and the way it processes non UI threads I believe. So I have added the following to my web.config (see below)

  • UseTaskFriendlySynchronizationContext
  • enableSessionState="false"

Unfortunately it does not make any difference and my async / await call to LongProcess() is still blocking.

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

回答1:


I am new to the async / await features of C#5 but I feel sure these are meant for just this kind of task.

No. async/await are intended to make asynchronous programming easy within the context of a single process. They do not change the nature of HTTP communications.

When you use await on an ASP.NET server (in the context of an HTTP request), you are returning the ASP.NET thread to the thread pool. However, ASP.NET is aware that the HTTP request has not completed, so it does not complete the response to the client (browser).

This is by design.

You'll need to use AJAX, SignalR, or some other solution in order to do what you want.




回答2:


try setting the page attribute 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" %>

the in tcode behile file you can make the tasks and methods of type async and calll await tasks from them ...

it might work



来源:https://stackoverflow.com/questions/11039411/how-do-i-call-an-asynchronous-process-from-an-asp-net-hosted-module-using-c5-as

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!