我有一个带有一些子表单的winform。现在我通过主视图中的树视图访问子表单。现在,树视图是一个网站列表。由于网站实际上是一组页面,因此也会在树中显示。网站是我的父母,页面是孩子。
当您单击其中一个页面时,统计信息将以子表单形式显示。点击网站时,会显示该网站的所有页面摘要。由于这可能有很多数据,我使用后台工作程序在加载树视图后在后台获取静态。
现在一切正常,直到我在统计中更改某些内容并将其自动保存到数据库中(sqlserver2008)。然后我的backgroundworker会生成错误,说sqlreader已关闭。所以我想知道如何解决这个问题。
我是否必须在单独的连接上访问数据库?还有其他想法吗?
由于
答案 0 :(得分:4)
您是否在多个线程上使用相同的数据库连接对象?那不安全。每个线程必须有自己的数据库连接,或者它必须同步使用,以便在任何给定时刻只有一个线程访问数据库连接对象。
答案 1 :(得分:1)
听起来你的UPDATE正在关闭连接,你的后台线程正试图用它来查询。如果正确地在行之间读取,则已创建连接对象作为winform的有状态成员,该成员将被打开一次并且可能在UPDATE之后被关闭(或者由连接池自动关闭)。无论哪种方式,你都需要在这里退一步,思考你的工作方式。
您的更新和选择方法(希望在您的表单的单独类中)应该专门处理打开和关闭连接,而不是以有状态的方式(即连接的范围保留在方法中)。 ADO连接池将为您优化连接。
我还建议您使用断开连接的数据结构,例如DataTable和DataSet,而不是DataReader,因为它们保持客户端和数据库之间的连接足迹。您可以愉快地处理和传递这些结构,而无需维护DataReader所需的开放连接。理想情况下,你会填充一个轻量级的对象结构,而不是过多地传递DataSet,因为它确实带来了一些内存开销 - 还有很多其他原因,例如:类型安全。
调用在多个线程上执行查询的方法没有任何问题,只要连接的范围仅限于该方法即可。例如:
public static DataSet GetWebStatsData()
{
DataSet StatsData = new DataSet();
using (OleDbConnection conn = new OleDbConnection(ConnString))
{
using (OleDbCommand cm = new OleDbCommand(GetDataProcedure))
{
using (OleDbDataAdapter adap = new OleDbDataAdapter(cm))
{
cm.Connection = conn;
conn.Open();
adap.Fill(StatsData);
conn.Close();
}
}
}
return StatsData;
}
您可以愉快地同时从多个线程调用上述方法,它应该可以工作。我已经研究过大量使用线程池的应用程序,在某些情况下同时执行大量查询而没有任何不良影响。如果发生这种情况,我建议为ado.net连接池设置一个最大大小,因为如果没有防范,它可能会变得非常大(通常有一个连接字符串属性,如'Max Pool Size = n')。 Ado.net将代表您封送连接,因此在此框架中可以利用大量管道。
答案 2 :(得分:0)
我猜想导航事件是使用现有的sqlcommand触发新的数据库请求。你可以发布代码片段吗?
答案 3 :(得分:0)
如果您使用相同的连接字符串,则可以打开多个SqlConenction / SqlCommand / SqlDataReader对象,它们将来自连接池。没有理由你不能有多个连接到同一个数据库!您可以在SQL Server Connection Pooling (ADO.NET)了解更多信息。
答案 4 :(得分:0)
如果您尝试在多个线程上同时使用相同的SqlConnection,那肯定会产生不可预测的结果。为每个线程创建连接是最简单的,或者如果您真的想要共享单个连接,则可以使用某种形式的同步。只有当你的工作线程在数据库访问之外做了大量的工作时,这才有意义。