Aysnc方法仍然阻止ui界面

时间:2018-11-20 09:24:10

标签: c# winforms async-await

我有此方法,该方法使用ODBC并执行读取器异步操作,但由于某种原因,该方法仍阻塞我的UI,因为它正在加载4000条记录,但我想知道是否有人可以查看我的代码以查看我要去哪里。< / p>

async Task<BindingList<PurchaseLinkHeaderC>> GetPurchaseOrders( IProgress<int> progress)
{
    BindingList<PurchaseLinkHeaderC> _purhcaseOrderList = new BindingList<PurchaseLinkHeaderC>();
    try
    {
        string sageDsn = ConfigurationManager.AppSettings["SageDSN"];
        string sageUsername = ConfigurationManager.AppSettings["SageUsername"];
        string sagePassword = ConfigurationManager.AppSettings["SagePassword"];
        //using (var connection = new OdbcConnection("DSN=SageLine50v24;Uid=Manager;Pwd=;"))

        using (var connection =
            new OdbcConnection(String.Format("DSN={0};Uid={1};Pwd={2};", sageDsn, sageUsername, sagePassword)))
        {
            connection.Open();
            string fromD = dtpFrom.Value.ToString("yyyy-MM-dd");
            string toD = dtpTo.Value.ToString("yyyy-MM-dd");

            string SQL =
                "SELECT 'ORDER_NUMBER', 'ORDER_OR_QUOTE', 'ORDER_DATE', 'DELIVERY_DATE', 'ORDER_STATUS_CODE', 'ORDER_STATUS', 'DELIVERY_STATUS_CODE', 'DELIVERY_STATUS', 'ACCOUNT_REF', 'NAME', 'ADDRESS_1', 'ADDRESS_2', 'ADDRESS_3', 'ADDRESS_4', 'ADDRESS_5', 'C_ADDRESS_1', 'C_ADDRESS_2', 'C_ADDRESS_3', 'C_ADDRESS_4', 'C_ADDRESS_5', 'DEL_NAME', 'DEL_ADDRESS_1', 'DEL_ADDRESS_2', 'DEL_ADDRESS_3', 'DEL_ADDRESS_4', 'DEL_ADDRESS_5', 'VAT_REG_NUMBER', 'REFERENCE', 'CONTACT_NAME', 'TAKEN_BY', 'SUPP_ORDER_NUMBER', 'SUPP_TEL_NUMBER', 'NOTES_1', 'NOTES_2', 'NOTES_3', 'SUPP_DISC_RATE', 'FOREIGN_ITEMS_NET', 'FOREIGN_ITEMS_TAX', 'FOREIGN_ITEMS_GROSS', 'ITEMS_NET', 'ITEMS_TAX', 'ITEMS_GROSS', 'TAX_RATE_1', 'TAX_RATE_2', 'TAX_RATE_3', 'TAX_RATE_4', 'TAX_RATE_5', 'NET_AMOUNT_1', 'NET_AMOUNT_2', 'NET_AMOUNT_3', 'NET_AMOUNT_4', 'NET_AMOUNT_5', 'TAX_AMOUNT_1', 'TAX_AMOUNT_2', 'TAX_AMOUNT_3', 'TAX_AMOUNT_4', 'TAX_AMOUNT_5', 'COURIER_NUMBER', 'COURIER_NAME', 'CONSIGNMENT', 'CARR_NOM_CODE', 'CARR_TAX_CODE', 'CARR_DEPT_NUMBER', 'CARR_DEPT_NAME', 'FOREIGN_CARR_NET', 'FOREIGN_CARR_TAX', 'FOREIGN_CARR_GROSS', 'CARR_NET', 'CARR_TAX', 'CARR_GROSS', 'FOREIGN_INVOICE_NET', 'FOREIGN_INVOICE_TAX', 'FOREIGN_INVOICE_GROSS', 'INVOICE_NET', 'INVOICE_TAX', 'INVOICE_GROSS', 'CURRENCY', 'CURRENCY_TYPE', 'EURO_GROSS', 'EURO_RATE', 'FOREIGN_RATE', 'SETTLEMENT_DUE_DAYS', 'SETTLEMENT_DISC_RATE', 'FOREIGN_SETTLEMENT_DISC_AMOUNT', 'FOREIGN_SETTLEMENT_TOTAL', 'SETTLEMENT_DISC_AMOUNT', 'SETTLEMENT_TOTAL', 'PAYMENT_REF', 'PRINTED', 'PRINTED_CODE', 'POSTED', 'POSTED_CODE', 'QUOTE_STATUS_ID', 'RECURRING_REF', 'DUNS_NUMBER', 'PAYMENT_TYPE', 'BANK_REF', 'GDN_NUMBER', 'PROJECT_ID', 'ANALYSIS_1', 'ANALYSIS_2', 'ANALYSIS_3', 'INVOICE_PAYMENT_ID', 'RESUBMIT_INVOICE_PAYMENT_REQUIRED', 'RECORD_CREATE_DATE', 'RECORD_MODIFY_DATE', 'RECORD_DELETED' FROM 'PURCHASE_ORDER' WHERE ORDER_DATE >='{0}' and ORDER_DATE <='{1}'";

            int counter = 0;
            using (var command = new OdbcCommand(string.Format(SQL, fromD, toD), connection))
            {
                using (var reader = await command.ExecuteReaderAsync())
                {
                    while (await reader.ReadAsync())
                    {
                        var purhcaseOrders = new PurchaseLinkHeaderC();
                        if ((reader["ORDER_NUMBER"] != ""))
                        {
                            counter++;

                            string orderNumber = Convert.ToString(reader["ORDER_NUMBER"]);
                            purhcaseOrders.Order_Number = OrderNumber.ToString();
                            purhcaseOrders.PurchaseOrderNo = Convert.ToInt32(reader["ORDER_NUMBER"]);
                            purhcaseOrders.Name = reader["NAME"].ToString();
                            purhcaseOrders.Selected_PurchaseOrder = false;
                            _purhcaseOrderList.Add(purhcaseOrders);
                        }
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        var logger = NLog.LogManager.GetCurrentClassLogger();
        logger.Info(ex, "Error at GetSalesOrders " + ex.ToString());
    }

    return _purhcaseOrderList;
}

我希望能够显示正在加载的列表的进度条,所以我试图以此方式进行调用。但是我也不知道如何附加进度方法。

var progressIndicator = new Progress<int>(ReportProgress);
//call async method
BindingList<PurchaseLinkHeaderC> purchaseOrders = await GetPurchaseOrders(progressIndicator);
_masterPurchaseOrders = purchaseOrders;

我希望有人可以在这里提供帮助。

1 个答案:

答案 0 :(得分:2)

await时,默认行为是在从async操作返回时使用同步上下文(如果有)。对于UI应用程序,同步上下文为:UI。

所以;现在,有很多东西回到到UI。当上下文很重要时,此功能很有用,但对于您而言,则不然-无关紧要-因为您只是返回列表。

这意味着您应该可以将.ConfigureAwait(false)添加到其中的许多await表达式中,例如:

while (await reader.ReadAsync().ConfigureAwait(false))

断开同步上下文行为,并且可能会改善您所看到的内容。理想情况下,您可以将其添加到实用程序方法中的await调用GetPurchaseOrders)的 all 中。

您可能还希望查找任何 missing async操作-例如,connection.Open();可以是await connection.OpenAsync().ConfigureAwait(false);

请注意,呼叫代码不应使用ConfigureAwait(false)-由于绑定列表会触摸UI,因此需要同步上下文。因此:请勿将ConfigureAwait(false)添加到await GetPurchaseOrders(...)调用中。


还有另一种可能性:您说您正在使用ODBC和“ sage”。 ODBC / sage API 完全不支持 await是完全有可能的,它被实现为“异步同步”。如果是这种情况,将变得很棘手。在这种情况下,您可能需要使用 thread 而不是async / await-也许通过ThreadPool.QueueUserWorkItem。有多种方法可以在工作线程上调用async代码,但是如果“异步”代码实际上是“假装为异步的同步代码”,那么实际上没有任何意义,您也可以这样做。办法”。这通常意味着:

  • 开始工作(ThreadPool
  • 在工作程序上做一些工作(您现有的代码,但也许使用非异步实现)
  • 在工作程序的末尾,使用Control.Invoke将工作推回UI线程以进行最后的“更新UI”步骤