【问题标题】:ASMX async System.InvalidOperationException: An asynchronous operation cannot be started at this timeASMX async System.InvalidOperationException:此时无法启动异步操作
【发布时间】:2022-09-28 14:23:15
【问题描述】:

在使一些旧的 Web 服务(ASMX - ASp.Net)异步时,在从 javascript 对服务进行 ajax 调用后,我遇到了浏览器中出现错误消息的问题:

    Error (RetrieveDD): {
  \"readyState\": 4,
  \"responseText\": \"System.InvalidOperationException: An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%@ Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it.\\r\\n   at System.Web.AspNetSynchronizationContext.OperationStarted()\\r\\n   at System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()\\r\\n   at SearchWebsite.DDService.LoadCountries(Int32 ID)\\r\\n\",
  \"status\": 500,
  \"statusText\": \"error\"
}

javascript方面:

        if (servicename != \"\") {
        $.ajax({
            url: \'../../DDService.asmx/\' + servicename,
            dataType: \'json\',
            method: \'post\',
            data: { ID: id },
            success: function success(data) {
                ddl.empty();
                if (hid != \"\") {
                    $.each(data, function () {
                        if (this[\'V\'] == hid) {
                            var newOption = new Option(this[\'T\'], this[\'V\'], true, true);
                            ddl.append(newOption);
                        } else {
                            var newOption = new Option(this[\'T\'], this[\'V\'], false, false);
                            ddl.append(newOption);
                        }
                    });
                } else {
                    $.each(data, function (index) {
                        if (index == 0) {
                            var newOption = new Option(this[\'T\'], this[\'V\'], true, true);
                            ddl.append(newOption);
                        } else {
                            var newOption = new Option(this[\'T\'], this[\'V\'], false, false);
                            ddl.append(newOption);
                        }
                    });
                };
                if (typeof callback === \'function\') {
                    callback(data);
                };
            },
            error: function error(err) {
                console.log(\'Error (RetrieveDD): \' + JSON.stringify(err, null, 2));
                if (typeof callback === \'function\') {
                    callback(err);
                }
            }
        });
    };

以及webservice中的方法(旧的asmx类型):

    [WebMethod(Description = \"Return list of Countries\")]
    public async void LoadCountries(int ID)
    {          
        var retval = new List<DDListItemLight>();
        retval = await DropDownData.GetCountriesListAsync();
        string responseStr = JsonConvert.SerializeObject(retval);
        Context.Response.Write(responseStr);
    }

无论如何,在我使 webmethod 异步之前,一切正常。 我尝试将签名更改为 公共异步任务 和 公共异步任务 也没有运气。 我认为结束响应会起作用,但它没有效果。

在这里和互联网上环顾四周时,建议将 Async=\"true\" 放在页面标题上 - 但这是一个网络方法而不是 ASPX 页面,所以我希望有一些方法可以使这项工作(使用旧 asmx)。 对此的 MS 文档已有十年之久,并且早于 async/await(我找不到任何示例)

有任何想法吗?

    标签: c# asp.net asynchronous webforms asmx


    【解决方案1】:

    ASMX 不支持async。在引入async 时,它已经有好几代了,所以它从未更新为支持async

    It does support APM-style asynchrony,所以你可以让它工作,但这会很尴尬。

    首先,您需要确保您的目标是 .NET 4.5 或更高版本以及have httpRuntime.targetFramework set to 4.5 or higher

    然后,您可以使用 APM 方法定义您的 API,并包装更自然的 TAP 方法。包装可能有点痛苦,所以我建议使用我的AsyncEx library 中的ApmTaskFactory,如下所示:

    [WebMethod(Description = "Return list of Countries")]
    public IAsyncResult BeginLoadCountries(int ID, AsyncCallback callback, object state)
    {
      var task = LoadCountriesAsync(ID);
      return ApmAsyncFactory.ToBegin(task, callback, state);
    }
    
    private static async Task<string> LoadCountriesAsync(int ID)
    {
      var retval = new List<DDListItemLight>();
      retval = await DropDownData.GetCountriesListAsync();
      return JsonConvert.SerializeObject(retval);
    }
    
    public void EndLoadCountries(IAsyncResult asyncResult)
    {
      var responseStr = ApmAsyncFactory.ToEnd<string>(asyncResult);
      Context.Response.Write(responseStr);
    }
    

    【讨论】:

    • 尽管我认为自己在 DotNet 方面拥有丰富的经验,但我什至从未听说过 APM。哇!谢谢!
    【解决方案2】:

    虽然接受的答案有效,但我决定尝试一些不同的东西并使用像 MVC 这样的控制器。 (最终将转换为 Blazor WASM,因此编写这些控制器并不是浪费时间)

    这是控制器类:

    public class DDServiceController : ApiController
    {
        private static string DbConn;
        DropDownData dropDownData = new DropDownData();
    
        public DDServiceController()
        {
            DbConn = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
            dropDownData.SetDBConnStr(DbConn);
        }
    
        [Route("api/LoadCountries")]
        [HttpGet]
        public async Task<IHttpActionResult> LoadCountries(int id)
        {
            var retval = new List<DDListItemLight>();
            retval = await dropDownData.GetCountriesListAsync();
            return Ok(retval);
        }
    
        [Route("api/LoadStateProvinces")]
        [HttpGet]
        public async Task<IHttpActionResult> LoadStateProvinces(int id)
        {
            var retval = new List<DDListItemLight>();
            retval = await dropDownData.GetStateProvincesByCountryListAsync(id);
            return Ok(retval);
        }
    
        [Route("api/LoadCityLocalities")]
        [HttpGet]
        public async Task<IHttpActionResult> LoadCityLocalities(int id)
        {
            var retval = new List<DDListItemLight>();
            retval = await dropDownData.GetCityLocalitiesByState_ProvinceListAsync(id);
            return Ok(retval);
        }
    
        [Route("api/loadsalutations")]
        [HttpGet]
        public async Task<IHttpActionResult> LoadSalutations(int id)
        {
            var retval = new List<DDListItemLight>();
            retval = await dropDownData.GetSalutationsListAsync();
            return Ok(retval);
        }
    
    }
    

    然后像这样从js调用它:

        function RetrieveDD(ddl, ddtype, id) {
        var callback = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
    
        var servicename;
        var hid;
        switch (ddtype) {
            case 'salutation':
                servicename = "LoadSalutations";
                hid = $("#hidSelectedSalutation").val();
                break;
            case 'country':
                servicename = "LoadCountries";
                hid = $("#hidSelectedCountry").val();
                break;
            case 'state':
                servicename = "LoadStateProvinces";
                hid = $("#hidSelectedState").val();
                break;
            case 'city':
                servicename = "LoadCityLocalities";
                hid = $("#hidSelectedCity").val();
                break;
            case 'salutation':
                servicename = "LoadSalutations";
                hid = $("#hidSelectedSalutation").val();
                break;
            default:
                hid = "";
                servicename = "";
        }
        if (servicename != "") {
            $.ajax({
                url: '../../api/' + servicename,
                dataType: 'json',
                method: 'get',
                data: { ID: id },
                success: function success(data) {
                    ddl.empty();
                    if (hid != "") {
                        $.each(data, function () {
                            if (this['V'] == hid) {
                                var newOption = new Option(this['T'], this['V'], true, true);
                                ddl.append(newOption);
                            } else {
                                var newOption = new Option(this['T'], this['V'], false, false);
                                ddl.append(newOption);
                            }
                        });
                    } else {
                        $.each(data, function (index) {
                            if (index == 0) {
                                var newOption = new Option(this['T'], this['V'], true, true);
                                ddl.append(newOption);
                            } else {
                                var newOption = new Option(this['T'], this['V'], false, false);
                                ddl.append(newOption);
                            }
                        });
                    };
                    if (typeof callback === 'function') {
                        callback(data);
                    };
                },
                error: function error(err) {
                    console.log('Error (RetrieveDD): ' + JSON.stringify(err, null, 2));
                    if (typeof callback === 'function') {
                        callback(err);
                    }
                }
            });
        };
    };
    

    【讨论】:

      猜你喜欢
      • 2016-05-14
      • 2019-01-11
      • 2013-06-10
      • 1970-01-01
      • 1970-01-01
      • 2020-04-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多