【问题标题】:Convert code from BGW to Async and Await for DataTabale/MySQLDataAdapter将代码从 BGW 转换为异步并等待 DataTabale/MySQLDataAdapter
【发布时间】:2014-12-11 13:14:05
【问题描述】:

我如何放弃 BGW 并使用异步/等待任务的东西,我愿意在使用 VS 2013 时学习该技术。

我在网上查看了示例,但我仍然无法自己做,因为我遇到的示例使用 .NET 中已经返回任务的现有函数。我试图在 BGW DoWork 中分离代码并创建一个任务,但编译器一直问我等待,我还是无法调用该任务,我注意到需要时间的行是:

SQLDataAdapter = new MySqlDataAdapter(SQLcmd);
SQLDataAdapter.Fill(dt);

我需要的是:在一个按钮里面点击开始读取数据库的任务,然后解除Form的挂起(假设我没有使用BGW),然后读取结果并显示在datagridview中。

// code starts here
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using MySql.Data.MySqlClient;
using System.IO;

namespace MySQLProject
{
    public partial class Form1 : Form
    {


        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            string stdNo = File.ReadAllText("stdNo.txt").Replace(Environment.NewLine, ",");


            const string cs = @"what ever";
            MySqlConnection conn = new MySqlConnection(cs);
            MySqlDataAdapter SQLDataAdapter = new MySqlDataAdapter(); ;
            DataSet ds = new DataSet();
            conn.Open();
            this.InvokeEx(x => x.textBox1.AppendText(string.Format("MySQL version : {0};", conn.ServerVersion)));

            DataTable dt = new DataTable("StudentNamesAndNumbers");
            dt.Columns.Add("Student Name", typeof(string));
            dt.Columns.Add("Student ID", typeof(string));
            dt.Columns.Add("First", typeof(float));
            dt.Columns.Add("Second", typeof(float));
            dt.Columns.Add("Section", typeof(string));
            ds.Tables.Add(dt);

            try
            {

                MySqlCommand SQLcmd = new MySqlCommand();
                SQLcmd = conn.CreateCommand();
                SQLcmd.CommandText = String.Format(@"Select u.firstname as 'Student Name', u.username as 'Student ID'"
                                                    + ",( select  score from gradebook_result g , gradebook_evaluation e "
                                                    + "where g.user_id = u.user_id "
                                                    + "and name = 'First' "
                                                    + "and g.evaluation_id = e.id "
                                                    + "and e.course_code = c.course_code) as 'First' "
                                                    + ",( select  score from gradebook_result g , gradebook_evaluation e "
                                                    + "where g.user_id = u.user_id "
                                                    + "and name = 'Second' "
                                                    + "and g.evaluation_id = e.id "
                                                    + "and e.course_code = c.course_code) as 'Second' "
                                                    + ", c.course_code as 'Section'"
                                                    + "from user u, course_rel_user c "
                                                    + "where "
                                                    + "u.username in ({0}) "
                                                    + "and u.username REGEXP '[0-9]+' "
                                                    + "and c.course_code like 'IT102CPLUS%' "
                                                    + "and  u.user_id = c.user_id ;", stdNo);

                this.InvokeEx(x => x.textBox1.AppendText(SQLcmd.CommandText));

                SQLDataAdapter = new MySqlDataAdapter(SQLcmd);
                SQLDataAdapter.Fill(dt);
                dt.DefaultView.Sort = "Section ASC, Student Name ASC";
                this.InvokeEx(x => x.dataGridView1.Columns.Clear());
                this.InvokeEx(x => x.dataGridView1.DataSource = ds.Tables["StudentNamesAndNumbers"]);
                this.InvokeEx(x => x.dataGridView1.AutoResizeColumns());
                this.InvokeEx(x => x.label1.Text = dt.Rows.Count.ToString() + " Students");

                // =======================================================

                var lines = new List<string>();

                string[] columnNames = dt.Columns.Cast<DataColumn>().
                                                  Select(column => column.ColumnName).
                                                  ToArray();

                var header = string.Join(",", columnNames);
                lines.Add(header);

                var valueLines = dt.AsEnumerable().Select(row => string.Join(",", row.ItemArray));            
                lines.AddRange(valueLines);

                File.WriteAllLines("Export.csv", lines, Encoding.UTF8);

            }
            catch (MySqlException ex)
            {
                this.InvokeEx(x => x.textBox1.AppendText(string.Format("Error: {0}\n\n", ex.ToString())));
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                }
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            button1.Enabled = true;
        }
    }
}

【问题讨论】:

    标签: c# async-await task backgroundworker


    【解决方案1】:

    我有一个series on my blog,它显示了BackgroundWorkerTask.Run 的比较。

    一般来说,您不应该将这些方法中的任何一种用于 I/O 密集型操作。但是,有些较旧的 API 尚未升级以公开 TAP 方法。我相信“数据适配器”和“数据表”API 被认为太旧而无法升级,因此没有可以使用的“FillAsync”。

    所以 - 假设您想保留“数据表”方法 - 您可以直接使用 Task.Run 作为 BackgroundWorker 的替代品。请注意,IProgress&lt;T&gt; 是一种更现代的方式来执行任何类型的跨线程调用以获取进度更新:

    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        var progress = new Progress<string>(update => textBox1.AppendText(update));
        var ds = await Task.Run(() => BackgroundWork(progress));
        dataGridView1.Columns.Clear();
        dataGridView1.DataSource = ds.Tables["StudentNamesAndNumbers"];
        dataGridView1.AutoResizeColumns();
        label1.Text = dataGridView1.Rows.Count.ToString() + " Students";
        button1.Enabled = true;
    }
    
    private DataSet BackgroundWork(IProgress<string> progress)
    {
        string stdNo = File.ReadAllText("stdNo.txt").Replace(Environment.NewLine, ",");
    
        const string cs = @"what ever";
        MySqlConnection conn = new MySqlConnection(cs);
        MySqlDataAdapter SQLDataAdapter = new MySqlDataAdapter(); ;
        DataSet ds = new DataSet();
        conn.Open();
        if (progress != null)
            progress.Report(string.Format("MySQL version : {0};", conn.ServerVersion));
    
        DataTable dt = new DataTable("StudentNamesAndNumbers");
        dt.Columns.Add("Student Name", typeof(string));
        dt.Columns.Add("Student ID", typeof(string));
        dt.Columns.Add("First", typeof(float));
        dt.Columns.Add("Second", typeof(float));
        dt.Columns.Add("Section", typeof(string));
        ds.Tables.Add(dt);
    
        try
        {
            MySqlCommand SQLcmd = new MySqlCommand();
            SQLcmd = conn.CreateCommand();
            SQLcmd.CommandText = String.Format(@"Select u.firstname as 'Student Name', u.username as 'Student ID'"
                                                    + ",( select  score from gradebook_result g , gradebook_evaluation e "
                                                    + "where g.user_id = u.user_id "
                                                    + "and name = 'First' "
                                                    + "and g.evaluation_id = e.id "
                                                    + "and e.course_code = c.course_code) as 'First' "
                                                    + ",( select  score from gradebook_result g , gradebook_evaluation e "
                                                    + "where g.user_id = u.user_id "
                                                    + "and name = 'Second' "
                                                    + "and g.evaluation_id = e.id "
                                                    + "and e.course_code = c.course_code) as 'Second' "
                                                    + ", c.course_code as 'Section'"
                                                    + "from user u, course_rel_user c "
                                                    + "where "
                                                    + "u.username in ({0}) "
                                                    + "and u.username REGEXP '[0-9]+' "
                                                    + "and c.course_code like 'IT102CPLUS%' "
                                                    + "and  u.user_id = c.user_id ;", stdNo);
    
            if (progress != null)
                progress.Report(SQLcmd.CommandText);
    
            SQLDataAdapter = new MySqlDataAdapter(SQLcmd);
            SQLDataAdapter.Fill(dt);
            dt.DefaultView.Sort = "Section ASC, Student Name ASC";
            var lines = new List<string>();
    
            string[] columnNames = dt.Columns.Cast<DataColumn>().
                                                  Select(column => column.ColumnName).
                                                  ToArray();
    
            var header = string.Join(",", columnNames);
            lines.Add(header);
    
            var valueLines = dt.AsEnumerable().Select(row => string.Join(",", row.ItemArray));            
            lines.AddRange(valueLines);
    
            File.WriteAllLines("Export.csv", lines, Encoding.UTF8);
            return ds;
        }
        catch (MySqlException ex)
        {
            if (progress != null)
                progress.Report(string.Format("Error: {0}\n\n", ex.ToString()));
        }
        finally
        {
            if (conn != null)
            {
                conn.Close();
            }
        }
    }
    

    【讨论】:

    • 我花了大约 20 分钟试图理解您所写的内容。您的回答反映出您的书确实很棒! ^_^ ^_^ ^_^ ^_^ 玩完你的代码后我会回复你的:-)
    • 有效!我只是在单击按钮内将 dt 替换为 dataGridView1 以计算学生数。我尝试了很多次使用异步的东西,但我不知道我也必须将异步与 button_click 处理程序一起使用。顺便说一句,您所说的“数据表”方法是什么意思,我还能做什么?
    • @elekgeek:如今,大多数开发人员商店要么使用某种形式的 ORM(实体框架、NHibernate 或几十个小工具中的任何一种),要么直接使用 ADO。数据表和适配器有一些相当大的内存和性能开销,尤其是对于大型数据集。
    • 问题是,当我搜索如何使用 MySQL 和 C# 时,大多数人都使用 DataAdapter 或 DataReader。我从未听说过 ORM、实体框架、NHibernate。我会在这些方面做更多的搜索,谢谢。
    • 如何将行一一添加到数据网格并更新进度条?
    【解决方案2】:

    您应该将该方法标记为async,并尽可能等待异步方法。 如果没有异步方法并且您想在另一个线程上运行同步方法,请在 Task.Run 上等待。

        private async void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            try
            {
                //Use an async method when there is one available
                string stdNo;
                using (var reader = File.OpenText("Words.txt"))
                {
                    stdNo = await reader.ReadToEndAsync();
                }
                stdNo = stdNo.Replace(Environment.NewLine, ",");
    
                const string cs = @"what ever";
                MySqlConnection conn = new MySqlConnection(cs);
                MySqlDataAdapter SQLDataAdapter = new MySqlDataAdapter(); ;
                DataSet ds = new DataSet();
    
                //Use Task.Run to call a long running code on another thread
                //If available you should use await conn.OpenAsync();
                await Task.Run(() => conn.Open());
                //You don't need invoke, after an await you are back to the synchronization context
                textBox1.AppendText(string.Format("MySQL version : {0};", conn.ServerVersion));
    
                DataTable dt = new DataTable("StudentNamesAndNumbers");
                dt.Columns.Add("Student Name", typeof(string));
                dt.Columns.Add("Student ID", typeof(string));
                dt.Columns.Add("First", typeof(float));
                dt.Columns.Add("Second", typeof(float));
                dt.Columns.Add("Section", typeof(string));
                ds.Tables.Add(dt);
                //...
            }
            finally
            {
                button1.Enabled = true;
            }
    
    
        }
    

    【讨论】:

    • 你为什么要在 conn.Open() 上做,连接打开不需要时间,实际工作在SQLDataAdapter = new MySqlDataAdapter(SQLcmd);SQLDataAdapter.Fill(dt); 反正我怎么能在那些上做呢两行?
    • the connection opening doesn't take time你不知道需要多长时间,网络超时通常是60秒。您只需将相同的逻辑应用于其他调用。
    • @Guillaume 你可以使用真正异步的await conn.OpenAsync();'
    • 我可以这样做吗:await Task.Run(() =&gt; SQLDataAdapter = new MySqlDataAdapter(SQLcmd));
    • var myAdapter = await Task.Run(() => new MySqlDataAdapter(SQLcmd));
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-13
    • 1970-01-01
    • 2018-07-12
    • 1970-01-01
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    相关资源
    最近更新 更多