BackgroundWorker 在 foreach 循环期间 return 来自 DAL 的值

BackgroundWorker to return a value from DAL during foreach loop

我的视图中有一个后台工作者,它 运行 在我的 DAL 中是一个 sql 查询。

在我看来,我还有一个不断旋转的进度条,以便用户知道工作正在完成。

SQL 查询接收作为条件之一的 List<string>,我在其中一个条件中填充它,所以代码是这样的:

using (SqlConnection conn = new SqlConnection(_myHost))
        {
            conn.Open();

            foreach (string item in myList)
            {
                query = "select * from ( " +
                    "//big query with inner joins here" +
                    ") a " 

                using (SqlCommand cmd = new SqlCommand(query, conn))
                {
                    SqlDataReader reader = cmd.ExecuteReader();
                    dt.Load(reader);
                }
            }

因此,正如您在上面注意到的那样,我 运行 遍历列表中的所有项目,在 where 子句(上面省略)中单独查询它们,并将输出(如果有)添加到数据表中,一旦foreach 完成后,将returned 到我的视图层并成为gridview 的数据源。

我突然想到,虽然我无法查询服务器来告诉我查询完成了多少百分比,但也许我可以提供 foreach 循环的完成百分比,这对用户来说是一些信息。

我想弄清楚的是一种方法:

Return 在我看来,循环的当前迭代,我可以通过简单地添加一个 int i 计数器并在每个 foreach 上递增它来检测,或者可能是 string item在开始时通知正在查询的内容。

问题是,我不知道如何在代码执行时 return 我认为该值。我考虑过 C#4 元组,但它不会工作,因为它只在方法完成后才 returned,而不是在 foreach 执行期间。

有什么想法吗?

这是一种方法 - 将委托传递给后台线程上 运行 的方法。

public void Run(Action<int> update) 
{
    using (SqlConnection conn = new SqlConnection(_myHost))
    {
       conn.Open();

       int index = 0;

       foreach (string item in myList)
       {
          //Perform query

          //Notify the caller of progress
          update(index);

          index++
       }
    }
}

请注意,回调将在后台线程上执行。

编辑

我忘记了 BackgroundWorker 中内置的 ReportProgress 方法。

https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.reportprogress(v=vs.110).aspx

在 WinForms 应用程序中,BackgroundWorker 内部事务状态与应用程序通信的最简单方法是引发调用 ReportProgress method 的 ProgressChanged 事件,并在代码中适当的地方捕获它。

此事件是在您的表单所在的线程(UI 线程)中引发的,因此 trying to access UI components from a non UI thread 没有问题。

例如你可以这样写

// this code is the DoWork event of the BackgroundWorker
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker bkw = sender as BackgroundWorker;
    .....
    using (SqlConnection conn = new SqlConnection(_myHost))
    {
        conn.Open();
        int counter  = 1;
        int total = myList.Count;
        foreach (string item in myList)
        {
            .....

            WorkerItem item = new WorkerItem() { Counter = counter, Item = item};
            int progress = (100 * counter / total);
            bkw.ReportProgress(progress, item);
            counter++;
        }
    }
}

public class WorkerItem
{
    public int Counter {get;set;}
    public string Item {get;set;}
    ... add other properties if you like .....
}

然后您的 winform 代码可以启动 BackgroundWorker 并以这种方式处理 ReportProgress 事件

BackgroundWorker bkw = new BackgroundWorker();
bkw.WorkerReportsProgress = true;
bkw.WorkerSupportsCancellation = true;
bkw.ProgressChanged += bgw_ProgressChanged;
bkw.DoWork += bgw_DoWork;
bkw.RunWorkerAsync();
...

private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    WorkerItem item = e.UserState as WorkerItem;
    ... update a progress bar, show the current item in a label etc...
    Console.WriteLine($"Completed: {e.ProgressPercentage}% {WorkerItem.Counter} {WorkerItem.Item}");
}