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 方法。
在 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}");
}
我的视图中有一个后台工作者,它 运行 在我的 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 方法。
在 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}");
}