ASMX equivalent of RegisterAsyncTask

*爱你&永不变心* 提交于 2020-01-06 03:57:08

问题


When an ASPX page needs to make a call to a potentially long-running operation (lengthy DB query, call to a remote webservice, etc.), I use RegisterAsyncTask, so the IIS worker thread is returned to the pool, rather than being tied up for the duration of the long-running operation.

However ASMX webservices don't have a RegisterAsyncTask function. When an ASMX webservice needs to call a potentially long-running operation, how can I implement the same behavior as RegisterAsyncTask?

Note: the ASMX webservice is implemented as a script-service: returning json to a direct jQuery/ajax call. Therefore, I cannot use the "BeginXXX" approach described by MSDN, since that implements the asynchronous behavior within the generated client-stub (which isn't used when calling the webservice directly via ajax).

EDIT: Adding source code: implemented the BeginXXX/EndXXX approach listed in John's answer. The synchronous "Parrot" function works fine. But the asynchronous "SlowParrot" function gives an internal server error: "Unknown web method SlowParrot"

WebService1.asmx:

// Test class implemented according to: http://msdn.microsoft.com/en-us/library/aa480516.aspx
[WebService]
[ScriptService]
public class WebService1 : WebService
{
    // A normal, synchronous webMethod, to prove the overall webservice is working.
    [WebMethod(EnableSession = true)]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public string Parrot(string key)
    {
        return key;
    }

    // Artificially-slow function (uses Thread.Sleep).
    public string SleepyParrot(string key)
    {
        Thread.Sleep(10000);
        return key;
    }

    // Delegate matching our slow-running function.
    public delegate string SleepyParrotStub(string key);

    // State object to hold the delegate.
    public class MyState
    {
        public SleepyParrotStub Stub;
    }

    // Asynchronous web method, which should be accessible via: "Webservice1.asmx/SlowParrot".
    [WebMethod(EnableSession = true)]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public IAsyncResult BeginSlowParrot(string key, AsyncCallback callback, object asyncState)
    {
        SleepyParrotStub stub = new SleepyParrotStub(SleepyParrot);
        MyState ms = new MyState();
        ms.Stub = stub;
        return stub.BeginInvoke(key, callback, ms);
    }

    // Asynchronous web method, which should be accessible via: "Webservice1.asmx/SlowParrot".
    [WebMethod(EnableSession = true)]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public string EndSlowParrot(IAsyncResult result)
    {
        MyState ms = (MyState)result.AsyncState;
        return ms.Stub.EndInvoke(result);
    }
}

WebForm1.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication1.WebForm1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <script type="text/javascript" src="scripts/jquery-1.4.2.min.js"></script>
    <script type="text/javascript" src="scripts/json2.js"></script>
    <script type="text/javascript">
        function showHelloWorld() {
            $.ajax({
                type: "POST",
                url: "WebService1.asmx/Parrot",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                data: '{"key":"Hello World"}',
                success: myCallback,
                error: function(response) {
                    alert(response.statusText);
                }
            });
        }

        function showSomethingElse() {
            $.ajax({
                type: "POST",
                url: "WebService1.asmx/SlowParrot",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                data: '{"key":"something else"}',
                success: myCallback,
                error: function(response) {
                    alert(response.statusText);
                }
            });
        }

        function myCallback(response) {
            $("#myDiv").html(response.d);
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div id="myDiv"></div>
    <p><a href="javascript: showHelloWorld();">show hello world</a> | <a href="javascript: showSomethingElse();">show something else</a></p>
    </form>
</body>
</html>

回答1:


You need to make clear decision where do you want async invocation to happen and know exactly what runs where and which code gets generated. There's no such thing as magic :-)

Only functions with [WebMethod] are ever visible as web methods - with the exact names you gave them. So at the very least you'd have to invoke BeginSlowParrot and EndSlowParot but that wouldn't help you much since calling 1st will go to server Foo1 and the 2nd to server Foo2 in a web farm, not to mention that even with the same server (say sticky IP) you'd have to test if the actual object behind IAsyncResult is replayable from a JSON serialization (JSON is type-poor compared to XML so to speak). Even if it is, that still only works in a signle server scenario.

.NET libs know how to autogen proxy methods by following Begin/End as naming convention when they see them in a WSDL but it's still 2 actual web methods - do WebService1.asmx?WDSL from IE and you'see exactly what's declared.

If you want to invoke something asynchronously from the client on multiple threads (they are in IE not the server) then you need extra JavaScript code to do that - web methods themselves remain as they were. You'll need 2nd hidden DIV on the client that will handle only these slow calls and the 3rd and 4th - one for every additional kind of async interaction you want.

So my best guess is that you first need to confirm that 2 $ajax(...) calls are serialized indeed (you need 2 slow and different web methods to confirm that - each returning different value) and if so, you need to dig into included .js that contains $ajax and see what's going on there). It might be protecting itself from ppl trying to do full miltithreading in a browser and then complain that their data got damaged :-)

If you want something async on the server - that has to remain on that server - your client will still have to wait the response of one invocation it did but the web method code can spawn 5 async calls to run it's stuff, wait for them to finish, collect and merge results and then send them back.

Oh and if you want to copy something from an ASPX page you need to dig through the source generated for it (in a browser) and also know which things in your C# code generte what. ASP.NET tries very hard to hide that from you and confuse you as much as possible. It was done for ppl who hate client-server programming and want to pretend they are writing a local Windows app. So whatever you see there is totally private API and you have to look at generated page (tags and js code and includes) in you want to see what's going on.




回答2:


See "Asynchronous XML Web Service Methods" in the MSDN documentation.



来源:https://stackoverflow.com/questions/3322599/asmx-equivalent-of-registerasynctask

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