gpt4 book ai didi

c# - BackgroundWorkers 和 SQL 连接

转载 作者:太空宇宙 更新时间:2023-11-03 21:34:56 25 4
gpt4 key购买 nike

我希望有人可以就在不同线程中处理 SQL 连接时的最佳实践提供一些建议。

我现在遇到的让我问这个问题的问题(但我已经想了一段时间了)如下。

我有一个对话框需要一些时间来初始化,因为它包含一个从 SQL 数据库填充的控件。这不是一个庞大的查询,但它需要几秒钟,我认为用户没有理由在每次显示对话框或每次实例化包含对话框的表单时都等待那几秒钟。因此,我将填充对话框的代码放在 BackgroundWorker 中,这样它就可以在用户点击显示对话框的内容之前在后台发生。

到目前为止一切顺利。工作可爱。唯一的问题是,我的一个表单(使用此对话框)本身由一个 SQL 查询填充,而且,它涉及一个稍微复杂的查询(截断一个表并重新填充它),我想保持原子性我把它全部放在一个事务中。

但是现在我们遇到了问题;到这个事务开始时,对话框已经被实例化并且它的 BackgroundWorker 已经忙于填充它。所以我得到一个异常,上面写着

"New transaction is not allowed because there are other threads running in the session"

我能理解为什么会出现这样的异常,但我仍然不确定如何最好地解决它。

但可以肯定的是,这种在不同线程中针对同一数据库运行不同查询的概念(其中一些可能涉及事务)并不少见。我想这里肯定有人遇到过它,所以我希望有人能提供一些建议。

* 编辑 *好的,下面是一些代码来说明问题。提供完整的代码会过于复杂,我的问题实际上是关于 SQL 连接和多线程的原理,而不是关于我面临的这个特定问题,但如果它有助于理解问题,我还是会添加这段代码.

我有两个表单,我们称它们为 Form_MainForm_Dialog。当在前者上单击某个按钮时,后者显示为模式对话框。所以他们的胆量看起来像这样:

Form_Main.cs

public partial class Form_Main : Form
{
public Form_Main()
{
InitializeComponent();
// The above function is automatically created by the IDE and is
// defined in Form_Main.Designer.cs. It is inside this function
// that the class of the dialog form is defined and instantiated
// (and therefore its BackgroundWorker thread is started)

Populate();
}

private void Populate()
{
using (SqlTransaction sqlTransaction = ClassGlobal.sqlConnection.BeginTransaction())
{
using (SqlCommand sqlCommand = new SqlCommand(".....", ClassGlobal.sqlConnection, sqlTransaction))
{
// Here goes some code that populates the current
// form, and it's encapsulated inside a Transaction
}

sqlTransaction.Commit();
}
}

private void Button_Click(object sender, EventArgs e)
{
if (formDialog.ShowDialog() != DialogResult.OK)
return;

// Do some stuff with the return value from the dialog
}
}

和 Form_Dialog.cs

public partial class Form_Dialog : Form
{
public Form_Dialog()
{
InitializeComponent();

BackgroundWorker _populateWorker = new BackgroundWorker();
_populateWorker.DoWork += new DoWorkEventHandler(_populateWorker_DoWork);
_populateWorker.RunWorkerAsync();
}

void _populateWorker_DoWork(object sender, DoWorkEventArgs e)
{
using (SqlCommand sqlCommand = new SqlCommand(".....", ClassGlobal.sqlConnection, sqlTransaction))
{
// Here goes some code that populate the current form
}
}
}

这是 ClassGlobal.cs 的片段

public sealed class ClassGlobal
{
public static SqlConnection sqlConnection;

static ClassGlobal()
{
}

public static void DBInit(string server, string database, string uid, string password)
{
string connectionstring = "SERVER=" + server + ";" + "DATABASE=" + database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";

sqlConnection = new SqlConnection(connectionstring);
}
}

ClassGlobal 在应用程序早期实例化,DBInit 使用所需的连接参数调用。所有表单都可以访问其中定义的静态 SQLConnection

从上面可以看出,查看 Form_Main.cs,在调用 Populate() 函数时,InitializeComponent() 函数已经被调用,所以 Form_Dialog 的构造函数也被调用了,所以 Form_DialogBackgroundWorker 已经开始运转了。因此,无法启动 Form_MainPopulate() 函数中的事务。

最佳答案

是的,好的,我们可以看到问题所在。不要尝试共享 SqlConnection 对象。唯一应该共享和传递的是连接字符串

当你需要一个连接对象时,创建一个新的,打开,使用它,然后处理。让连接池(在幕后工作)负责实际使用了多少到服务器的实际物理连接。

例如:

void _populateWorker_DoWork(object sender, DoWorkEventArgs e)
{
using(var conn = new SqlConnection(ClassGlobal.ConnectionString
using (SqlCommand sqlCommand = new SqlCommand(".....", conn))
{
conn.Open();
// Here goes some code that populate the current form
}
}

和:

public sealed class ClassGlobal
{
public static string ConnectionString;

static ClassGlobal()
{
}

public static void DBInit(string server, string database, string uid, string password)
{
ConnectionString = "SERVER=" + server + ";" + "DATABASE=" + database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
}
}

关于c# - BackgroundWorkers 和 SQL 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22244086/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com