Метод Aysnc по-прежнему блокирует интерфейс - PullRequest
0 голосов
/ 20 ноября 2018

У меня есть этот метод, который использует ODBC и выполняет асинхронное чтение, но метод все еще блокирует мой пользовательский интерфейс по какой-то причине, он загружает 4000 записей, но мне интересно, может ли кто-нибудь взглянуть на мой код, чтобы увидеть, где я ошибаюсь.

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 голосов
/ 20 ноября 2018

Когда вы await, поведение по умолчанию по умолчанию заключается в использовании синхронизирующего контекста, если он есть, при возврате из операции async.В случае приложения с пользовательским интерфейсом контекст синхронизации: UI.

So;прямо сейчас есть много вещей, которые возвращаются в пользовательский интерфейс.Это полезно, когда контекст имеет значение, но в вашем случае это не так, поскольку вы просто возвращаете список.

Это означает, что вы должны иметь возможность добавить .ConfigureAwait(false) кмногие из этих await выражений - например:

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

Этот отключает поведение в контексте синхронизации и может улучшить то, что вы видите.В идеале вы должны добавить это к всем из await вызовов в служебном методе (GetPurchaseOrders).

Вы также можете поискать любой пропущено async операций - например, connection.Open(); может быть await connection.OpenAsync().ConfigureAwait(false);

Обратите внимание, что вызывающий код не должен используйте ConfigureAwait(false) - поскольку список привязок касается пользовательского интерфейса, для требуется контекст синхронизации.Итак: не добавляйте ConfigureAwait(false) к вызову await GetPurchaseOrders(...).


Существует также еще одна возможность: вы говорите, что используете ODBC и «sage».Вполне возможно, что ODBC / API sage не поддерживает await и реализуется как "синхронизация по асинхронному".Если это так, это становится сложно.В этом случае вам может понадобиться использовать thread вместо async / await - возможно, через ThreadPool.QueueUserWorkItem.Существуют способы вызова async кода в рабочих потоках, но если «асинхронный» код на самом деле является «синхронизирующим кодом, который претендует на асинхронность», на самом деле в этом нет никакого смысла, и вы могли бы также сделать это «по-старому»,Обычно это означает:

  • начать работника (ThreadPool)
  • выполнить некоторую работу над работником (существующий код, но, возможно, с использованием не асинхронной реализации)
  • в конце работника используйте Control.Invoke, чтобы отправить работу обратно в поток пользовательского интерфейса для последнего шага «обновить пользовательский интерфейс»
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...