C# Aysnc方法仍然阻止ui接口

C# Aysnc方法仍然阻止ui接口,c#,winforms,async-await,C#,Winforms,Async Await,我有一个使用ODBC并异步执行读卡器的方法,但由于某种原因,该方法仍然阻塞我的UI,它正在加载4000条记录,但我想知道是否有人可以查看我的代码,看看我哪里出了问题 async Task<BindingList<PurchaseLinkHeaderC>> GetPurchaseOrders( IProgress<int> progress) { BindingList<PurchaseLinkHeaderC> _purhcaseOrderL

我有一个使用ODBC并异步执行读卡器的方法,但由于某种原因,该方法仍然阻塞我的UI,它正在加载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;
}
异步任务GetPurchaseOrders(IProgress进程) { BindingList_purhcaseOrderList=新建BindingList(); 尝试 { 字符串sageDsn=ConfigurationManager.AppSettings[“sageDsn”]; 字符串sageUsername=ConfigurationManager.AppSettings[“sageUsername”]; 字符串sagePassword=ConfigurationManager.AppSettings[“sagePassword”]; //使用(var-connection=new-OdbcConnection(“DSN=SageLine50v24;Uid=Manager;Pwd=;”) 使用(var)连接= 新的OdbcConnection(String.Format(“DSN={0};Uid={1};Pwd={2};”,sageDsn,sageUsername,sagePassword))) { connection.Open(); 字符串fromD=dtpFrom.Value.ToString(“yyyy-MM-dd”); 字符串toD=dtpTo.Value.ToString(“yyyy-MM-dd”); 字符串SQL=
“选择‘订单编号’、‘订单编号’、‘订单日期’、‘交货日期’、‘订单状态代码’、‘订单状态’、‘交货状态代码’、‘交货状态’、‘账户编号’、‘名称’、‘地址1’、‘地址2’、‘地址3’、‘地址4’、‘地址5’、‘C地址1’、‘C地址2’、‘C地址3’、‘C地址4’、‘C地址5’、‘删除名称’、‘删除地址1’。”_地址2、删除地址3、删除地址4、删除地址5、增值税注册号、参考号、联系人姓名、取款人、供应商订单号、供应商电话号码、备注1、备注2、备注3、支持盘费率、国外项目净额、国外项目税、国外项目毛额、项目净额、项目税、项目毛额、税率1、税率2、税率税率3’、‘税率4’、‘税率5’、‘净金额1’、‘净金额2’、‘净金额3’、‘净金额4’、‘净金额5’、‘税金额1’、‘税金额2’、‘税金额3’、‘税金额4’、‘税金额4’、‘税金额5’、‘快递员编号’、‘快递员姓名’、‘寄售’、‘快递员姓名’、‘快递员税号’、‘快递员税号’、‘快递部’、‘快递员姓名’、‘国外快递员’、‘快递员’N________________________________________________________________________________________签署结算总额、结算盘金额、结算总额、付款参考、打印、打印代码、过账、过账代码、报价状态ID、定期参考、邓斯登编号、付款类型、银行参考、GDN编号、项目ID、分析1、分析2、分析3、发票付款ID、重新提交发票付款要求、记录RD_CREATE_DATE'、“RECORD_MODIFY_DATE”、“RECORD_DELETED”来自“PURCHASE_ORDER”,其中ORDER_DATE>={0}和ORDER_DATE当您等待时,默认行为是在从
异步操作返回时使用同步上下文(如果有)。对于UI应用程序,同步上下文是:UI

所以,现在,有很多东西回到了UI。当上下文重要时,这很有用,但在您的情况下:它不有用-因为您只是返回一个列表

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

while (await reader.ReadAsync().ConfigureAwait(false))
这将断开同步上下文行为的连接,并可能改善您看到的内容。您最好将其添加到实用程序方法(
GetPurchaseOrders
)中的所有
wait
调用中

您可能还希望查找任何缺少的
async
操作-例如,
connection.Open();
可以是
wait连接。OpenAsync().configurewait(false);

请注意,调用代码不应使用
ConfigureAwait(false)
-因为绑定列表接触UI,它需要同步上下文。因此:不要将
ConfigureAwait(false)
添加到
await GetPurchaseOrders(…)
调用中


还有另一种可能性:您说您正在使用ODBC和“sage”。完全有可能ODBC/sage API不支持
await
,它被实现为“异步同步”“。如果是这种情况,它会变得棘手。在这种情况下,您可能需要使用线程而不是
async
/
wait
——可能通过
ThreadPool.QueueUserWorkItem
。在工作线程上可以调用
async
代码,但如果“async”代码实际上是“假装异步的同步代码”,其实没有什么意义,你不妨用“老办法”来做。这通常意味着:

  • 启动辅助线程(
    ThreadPool
  • 在辅助程序上做一些工作(您现有的代码,但可能使用非异步实现)
  • 在工作线程结束时,使用
    Control.Invoke
    将工作推回到UI线程,以进行最后的“更新UI”步骤

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

所以,现在,有很多东西回到了UI。当上下文重要时,这很有用,但在您的情况下:它不有用-因为您只是返回一个列表

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

while (await reader.ReadAsync().ConfigureAwait(false))
这将断开同步上下文行为的连接,并可能改善您看到的内容。您最好将其添加到实用程序方法(
GetPurchaseOrders
)中的所有
wait
调用中

您可能还希望查找任何缺少的
async
操作,例如
connect