我有一个方法,它从带有引用的类调用另一个方法,而引用又返回一个数据表。
我正在尝试在线程中运行该查询,现在在线程中运行查询本身很容易,但是如何让它从线程返回数据表?
调用类的方法示例:
private void loadCombo(string sqlComand, string value, ComboBox loadBox)
{
DataTable dt = new DataTable();
//Thread thread = new Thread(() => sqlScript.loadCombo(sqlComand, value, loadBox));
//thread.start();
dt = sqlScript.loadCombo(sqlComand, value, loadBox);
loadBox.ValueMember = value;
loadBox.DataSource = dt;
loadBox.Refresh();
}
不要在另一个线程上执行数据库查询,而是使用async-await
方法,该方法将在一个线程上执行,并且不会阻止 UI。
为此,您需要将"数据查询"操作和ComboBox
设置分开
public class SqlScript
{
public async Task<DataTable> LoadDataAsync(string command, object value)
{
// Load data asynchronously by using ..Async methods
}
}
private async Task loadComboAsync(string sqlComand, string value, ComboBox loadBox)
{
var data = await sqlScript.LoadDataAsync(sqlComand, value);
loadBox.ValueMember = columnNameWhichRepresentValue;
loadBox.DataSource = data;
}
而且你不需要调用loadBox.Refresh
方法,设置新的数据源无论如何都可以。
请勿使用 Task.Run
.数据库操作不执行任何需要在另一个线程上执行的"繁重"操作。数据库操作仅发送命令并等待响应 - 因此仅使用另一个线程进行等待是浪费资源。
而是使用SqlConnection.OpenAsync
和SqlCommand.ExecuteReaderAsync
方法
public async Task<DataTable> loadCombo(string sqlCommand, string value)
{
var yourConnectionString = "DataSource=...";
using (var connection = new SqlConnection(yourConnectionString))
using (var command = new SqlCommand(sqlCommand, connection))
{
await connection.OpenAsync();
var reader = await command.ExecuteReaderAsync();
var data = new DataTable();
data.Load(reader);
return data;
}
}
听起来像是一个简单的问题。
您可以使用Task
(以及async/await
,但这可能是可选的,通常用于禁用/启用相应的按钮以防止多次执行(在事件处理程序中执行长时间运行的代码,而不会阻止 UI。您仍然需要调用 UI 元素的 acess:
async void loadCombo(string sqlComand, string value, ComboBox loadBox)
{
// disable button
await Task.Run(() =>
{
var dt = sqlScript.loadCombo(sqlComand, value, loadBox);
Invoke((Action)(() =>
{
loadBox.ValueMember = value; // is it out parameter? anyway, copying your code
loadBox.DataSource = dt;
loadBox.Refresh();
}));
}
// enable button
}
从另一个线程创建和传递DataTable
应该没有任何问题。
谢谢你@Sinatr和@Fabio,我测试了你的答案的组合,下面似乎给了我最快的返回时间。
主窗体方法调用:
private async Task loadCombo(string sqlCommand, string value, ComboBox loadBox)
{
var data = await sqlScript.loadCombo(sqlCommand, value);
loadBox.ValueMember = value;
loadBox.DataSource = data;
}
sqlScript 类方法:
public async Task<DataTable> loadCombo(string sqlCommand, string value)
{
SqlConnection myConn;
SqlCommand myCmd = default(SqlCommand);
SqlDataReader oResult;
DataTable dt = new DataTable();
await Task.Run(() =>
{
using (myConn = new SqlConnection("Data Source=" + frmMain.sqlServer + ";" + "Initial Catalog=" + frmMain.sqlData + ";User Id=" + frmMain.sqlUser + ";Password=" + frmMain.sqlPwd + ";"))
{
try
{
myConn.Open();
if (myConn.State == ConnectionState.Open)
{
myCmd = new SqlCommand((sqlCommand), myConn);
oResult = myCmd.ExecuteReader();
dt.Load(oResult);
myConn.Close();
}
else
{
}
}
catch (Exception err)
{
using (StreamWriter w = File.AppendText("ErrorLog.log"))
{
frmMain.Log("sqlScripts.loadCombo: " + err.Message, w);
}
}//End Try
}//End using
});
return dt;
}
谢谢@Sinatr,我只需要稍微调整一下你的答案,下面的代码似乎有效。
private async void loadCombo(string sqlCommand, string value, ComboBox loadBox)
{
await Task.Run(() =>
{
var dt = sqlScript.loadCombo(sqlCommand, value, loadBox);
Invoke((Action)(() =>
{
loadBox.ValueMember = value;
loadBox.DataSource = dt;
}));
});
}