调用Dapper ExecuteAsync时,Windows窗体GUI卡住了

时间:2016-07-26 15:25:48

标签: dapper c#-5.0

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

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

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

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

    MessageBox.Show("Done");
}

和async方法类似

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方法的调用会冻结用户界面。

我缺少什么?

2 个答案:

答案 0 :(得分:1)

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

当我点击" button2"时,GUI会按照您的描述冻结 - 单击button1不会增加标签中显示的数字。实际上,表单并不响应任何用户交互 - 尝试拖动该表单在查询完成之后将无效。

如果点击"按钮3"然后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服务器而不是Access(我没有Access可以在我的计算机上进行测试)。

我尝试通过删除"使用"来更改我的代码以使其与您的代码更相似围绕conn引用,调用OpenAsync,调用ConfigureAwait并在使用后显式关闭它但我无法重现该问题。我尝试使用和不使用ConfigureAwait(因为在工作完成后将false传递给它会导致问题,因为从非GUI线程尝试执行label1.Text更新是不允许的 - 将true传递给ConfigureAwait可以避免此问题)..但是我不能。

我建议你尝试在SQL Server实例上运行我的代码,如果你有一个可用的,只是为了让你自己确信你正在使用Dapper / async。然后尝试更改连接字符串(和SQL查询),以便它查询您的Access数据库,看看是否是问题的原因(如果Access 能够打破异步,我会感到惊讶方式,但我不知道它可能是什么)。

答案 1 :(得分:0)

事实证明,Dapper只是包装了ADO.NET异步方法,而OpenAync和ExecuteNonQueryAsync等方法的OLEDB默认实现只是为了同步运行它们!与SQL Server不同,SQL Server具有适当的异步方法实现。