【问题标题】:Windows forms GUI stuck when calling Dapper ExecuteAsync调用 Dapper ExecuteAsync 时 Windows 窗体 GUI 卡住
【发布时间】:2016-12-23 11:10:30
【问题描述】:

我正在尝试使用 Dapper 异步功能来保持我的 Windows 窗体客户端应用程序响应。我调用了 asycn 方法,但 GUI 卡住了,因为它是同步代码。

我正在使用一个访问 mdb 数据库,并且我的数据访问代码在一个类库中,但这里我有一个更简单的案例来演示这个问题。

在表单中,我有一个按钮单击事件处理程序,例如:

private async void button1_Click(object sender, EventArgs e)
{
    await DBTest();

    MessageBox.Show("Done");
}

异步方法类似于

private static async Task DBTest()
{
    OleDbConnection connection = new OleDbConnection();

    //Connection string is stored somewhere
    connection.ConnectionString = connectionString;

    await connection.OpenAsync().ConfigureAwait(false);

    //GUI stuck here until ExecuteAsync is done!
    await connection.ExecuteAsync("Delete from table1"); 

    connection.Close();
}

对 ExecuteAsync 方法的调用会冻结 UI。

我错过了什么?

【问题讨论】:

  • “卡住”和“冻结”是什么意思?

标签: dapper c#-5.0


【解决方案1】:

我试图通过创建新的 Windows 窗体应用程序并与 SQL Server 数据库(经典的NORTHWND database)通信来重现您所看到的内容。我有一个带有单个标签和三个按钮的表单 - 第一个按钮只是增加标签中显示的值,以便在表单响应或不响应时轻松测试。第二个按钮执行阻塞查询,第三个按钮执行异步查询。所有的代码都在下面..

当我单击“button2”时,GUI 像您描述的那样被冻结 - 单击 button1 不会增加标签中显示的数字。事实上,表单不会响应任何用户交互 - 在查询完成之前,试图拖动该表单是行不通的。

如果单击“button3”,则 GUI没有锁定(单击“button1”有效,拖动和调整表单大小仍然有效)。

using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Windows.Forms;
using Dapper;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private int _counter = 0;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _counter++;
            label1.Text = _counter.ToString();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
            {
                var timer = new Stopwatch();
                timer.Start();
                label1.Text = "Starting..";
                conn.Execute(@"
                    SELECT *
                    FROM Categories
                    INNER JOIN Customers
                    ON 1=1
                    INNER JOIN Employees
                    ON 1=1
                ");
                timer.Stop();
                label1.Text = "Done: " + timer.ElapsedMilliseconds + "ms";
            }
        }

        private async void button3_Click(object sender, EventArgs e)
        {
            using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
            {
                var timer = new Stopwatch();
                timer.Start();
                label1.Text = "Starting (async)..";
                await conn.ExecuteAsync(@"
                    SELECT *
                    FROM Categories
                    INNER JOIN Customers
                    ON 1=1
                    INNER JOIN Employees
                    ON 1=1
                ");
                timer.Stop();
                label1.Text = "Done (async): " + timer.ElapsedMilliseconds + "ms";
            }
        }
    }
}

我可以看到我的代码和你的代码之间的唯一区别是我将连接的打开保留给 ExecuteAsync 调用(因此我不需要使用 OpenAsync)并且我使用的是 SQL Server 而不是 Access (我在这里没有可在我的计算机上测试的访问权限)。

我尝试通过删除 conn 引用周围的“使用”、调用 OpenAsync、调用 ConfigureAwait 并在使用后显式关闭它,将我的代码更改为与您的更相似,但我无法重现该问题。我在有和没有 ConfigureAwait 的情况下都尝试过(因为在工作完成后将 false 传递给 this 会导致问题,因为 label1.Text 更新是从不允许的非 GUI 线程尝试的 - 将 true 传递给 ConfigureAwait 可以避免这个问题).. 但是我做不到。

如果您有可用的 SQL Server 实例,我建议您尝试针对 SQL Server 实例运行我的代码,以确保您正确使用 Dapper / async。然后尝试更改连接字符串(和 SQL 查询),以便它查询您的 Access 数据库并查看这是否是问题的原因(如果 Access is 能够在此中断异步,我会感到惊讶方式,但我不知道还有什么可能)。

【讨论】:

    【解决方案2】:

    原来 Dapper 只是封装了 ADO.NET 异步方法,OLEDB 对 OpenAsync 和 ExecuteNonQueryAsync 等方法的默认实现只是为了同步运行它们!不像 SQL Server,它对异步方法有适当的实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多