首页 > 解决方案 > 如何防止一种异步方法垄断另一种方法?

问题描述

在我的 UWP 应用程序中,我有一个异步方法(事件处理程序),它调用另一个异步方法,该方法尝试将记录插入数据库。

我在插入尝试中遇到了异常,并试图解释为什么会发生这种情况。所以我在 InsertMapRecord() 方法的第一个“使用”行上放了一个断点:

using (SqliteConnection conn = new SqliteConnection(connStr))

当我到达那个断点时,我按了 F10,但不是将我带到 Insert 方法中的下一行,而是将我带到 btnCre8NewMap_Click() 中的这一行,事件处理程序(您会认为它已经被击中,因为已到达上一行):

InsertMapRecord(mapName, mapNotes, defaultZoomLevel);

然后我按了 F11,试图返回 InsertMapRecord() 方法,但我最终在 App.gics 中,在这一行:

#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
            UnhandledException += (sender, e) =>
            {
                if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
            };
#endif

...突出显示“ global::System.Diagnostics.Debugger.Break() ”,然后显示以下异常消息:

在此处输入图像描述

完整的方法如下

private async void btnCre8NewMap_Click(object sender, RoutedEventArgs e)
{
    try
    {
        string mapName = string.Empty;
        string mapNotes = string.Empty;
        int defaultZoomLevel = 1;
        ClearLocations();
        // Popul8 the cmbx
        for (int i = 1; i < 20; i++)
        {
            cmbxCre8MapZoomLevels.Items.Add(i.ToString());
        }
        ContentDialogResult result = await cntDlgCre8Map.ShowAsync();

        if (result == ContentDialogResult.Primary)
        {
            mapName = txtbxMapName.Text;
            mapNotes = txtbxMapNotes.Text;
            defaultZoomLevel = cmbxCre8MapZoomLevels.SelectedIndex + 1;
            InsertMapRecord(mapName, mapNotes, defaultZoomLevel);
        }
        // else do nothing (don't save)
    }
    catch (Exception ex)
    {
        MessageDialog exceptionMsgDlg = new MessageDialog(ex.Message, "btnCre8NewMap_Click");
        await exceptionMsgDlg.ShowAsync();
    }
}

private async void InsertMapRecord(string mapName, string mapNotes, int preferredZoomLevel)
{
    path = folder.Path;
    connStr = string.Format(connStrBase, path);
    try
    {
        using (SqliteConnection conn = new SqliteConnection(connStr))
        {
            String query = "INSERT INTO dbo.CartographerMain " +
                "(MapName, MapNotes, PreferredZoomLevel) " +
                "VALUES (@MapName, @MapNotes, @PreferredZoomLevel)";

            using (SqliteCommand cmd = new SqliteCommand(query, conn))
            {
                cmd.Parameters.AddWithValue("@MapName", mapName);
                cmd.Parameters.AddWithValue("@MapNotes", mapNotes);
                cmd.Parameters.AddWithValue("@PreferredZoomLevel", preferredZoomLevel);
                conn.Open();
                int result = cmd.ExecuteNonQuery();

                if (result < 0)
                {
                    MessageDialog dialog = new MessageDialog("Error inserting data into CartographerMain");
                    await dialog.ShowAsync();
                }
            }
        }
    }
    catch (SqliteException sqlex)
    {
        MessageDialog dialog = new MessageDialog(sqlex.Message, "InsertMapRecord");
        await dialog.ShowAsync();
    }
}

标签: c#sqliteuwpasync-await

解决方案


InsertMapRecord方法应该返回一个Task可以由调用者等待的。此外,当您打开与数据库的连接或执行查询时,它不应阻塞:

private async Task InsertMapRecord(string mapName, string mapNotes, int preferredZoomLevel)
{
    path = folder.Path;
    connStr = string.Format(connStrBase, path);
    try
    {
        using (SqliteConnection conn = new SqliteConnection(connStr))
        {
            String query = "INSERT INTO dbo.CartographerMain " +
                "(MapName, MapNotes, PreferredZoomLevel) " +
                "VALUES (@MapName, @MapNotes, @PreferredZoomLevel)";

            using (SqliteCommand cmd = new SqliteCommand(query, conn))
            {
                cmd.Parameters.AddWithValue("@MapName", mapName);
                cmd.Parameters.AddWithValue("@MapNotes", mapNotes);
                cmd.Parameters.AddWithValue("@PreferredZoomLevel", preferredZoomLevel);
                await conn.OpenAsync();
                int result = await cmd.ExecuteNonQueryAsync();

                if (result < 0)
                {
                    MessageDialog dialog = new MessageDialog("Error inserting data into CartographerMain");
                    await dialog.ShowAsync();
                }
            }
        }
    }
    catch (SqliteException sqlex)
    {
        MessageDialog dialog = new MessageDialog(sqlex.Message, "InsertMapRecord");
        await dialog.ShowAsync();
    }
}

void应避免使用异步方法(事件处理程序除外)。

在您的事件处理程序中,您应该等待该InsertMapRecord方法和任何其他异步方法:

if (result == ContentDialogResult.Primary)
{
    mapName = txtbxMapName.Text;
    mapNotes = txtbxMapNotes.Text;
    defaultZoomLevel = cmbxCre8MapZoomLevels.SelectedIndex + 1;
    await InsertMapRecord(mapName, mapNotes, defaultZoomLevel);
}

如果您这样做,您应该能够捕获任何异常并进一步调查。


推荐阅读