我们有一个winforms应用程序每几秒钟调用一次存储过程。存储过程始终返回具有0行或更多行的结果集,并且客户端填充数据集。每隔几天左右我们就会发现数据集中没有表格,我们无法弄清楚原因。触发存储过程的过程确实发生在一个线程中,但数据库连接和存储过程的执行都发生在同一个线程中,因此这里的线程之间没有传递任何数据。
之前有没有人经历过这样的行为?
这是相关的代码,它是更大的应用程序的一部分,所以我试图只包括相关的位。
//Used by the main application to poll a database in a thread
public class Poller
{
private Thread thProcess_m;
private readonly Action<Exception> actOnError_m;
private readonly RuntimeData pRuntime_m;
private readonly DataAccessLayer dalDB_m;
private bool bRetry_m;
public Poller(RuntimeData pData, Action<Exception> actOnError)
{
this.thProcess_m = new Thread(Main);
this.thProcess_m.Name = "Main ScriptOr Polling Thread";
this.actOnError_m = actOnError;
this.dalDB_m = new DataAccessLayer(ConfigurationState.ConnectionStrings.DB);
}
public void Start()
{
this.thProcess_m.Start();
}
protected override void Main()
{
while (this.pRuntime_m.Running)
{
try
{
int iQueued;
Task pTask = this.dalDB_m.GetNextTask(out iQueued);
}
catch (Exception ex)
{
this.actOnError_m(ex);
}
}
}
}
public class RuntimeData
{
private bool bRunning_m;
public bool Running
{
get
{
return bRunning_m;
}
set
{
bRunning_m = value;
}
}
}
public class DataAcessLayer
{
public Task GetNextTask(out int iQueued)
{
iQueued = 0;
Task tskNext = null;
SqlCommand cmdNextTask = new SqlCommand();
cmdNextTask.CommandType = System.Data.CommandType.StoredProcedure;
cmdNextTask.CommandText = "pGetNextTask";
cmdNextTask.Connection = new System.Data.SqlClient.SqlConnection();
cmdNextTask.Connection.ConnectionString = "my connection string";
cmdNextTask.Connection.Open();
DataSet dsNextTask = new DataSet();
try
{
System.Data.SqlClient.SqlDataAdapter sqlNextTask = new System.Data.SqlClient.SqlDataAdapter(cmdNextTask);
sqlNextTask.Fill(dsNextTask);
}
finally
{
cmdNextTask.Connection.Close();
}
tskNext = LoadTask(dsNextTask);
if (dsTask.Tables[0].Rows.Count > 0)
{
iQueued = (int)dsTask.Tables[0].Rows[0]["Queued"];
}
return tskNext;
}
protected Task LoadTask(DataSet dsTask)
{
Task tskNext = null;
if (dsTask == null)
{
throw new ArgumentNullException("LoadTask DataSet is null.");
}
if (dsTask.Tables == null)
{
throw new NullReferenceException("LoadTask DataSet.Tables is null.");
}
//Here's where the exception is being thrown
if (dsTask.Tables.Count == 0)
{
throw new ArgumentOutOfRangeException("LoadTask DataSet.Tables.Count == 0.");
}
if (dsTask.Tables[0].Rows.Count > 0)
{
DataRow drTask = dsTask.Tables[0].Rows[0];
tskNext = new InteriorHealth.ScriptOr.ScriptTask((int)(drTask["id"]));
tskNext.Name = pRow["Name"].ToString();
}
return pTask;
}
}
答案 0 :(得分:1)
发生类似情况的一种可能性是存储过程调用根本不返回。如果它超时,它将不会返回任何内容,并且该过程根本不会达到填充数据集的程度。但是,如果没有看到您尝试做的一些示例代码,那么很难提供有效的答案。
答案 1 :(得分:1)
我遇到过这样的事情的唯一一次发生在我身上的事情就是我的存储过程中的错误处理逻辑在处理错误方面不是很好。
例如:
IF EXISTS(SELECT * FROM SOMEWHERE WHERE MYTHING = @WHATIWANT)
BEGIN
//select stuff and do weird things
END
IF @@ERROR <> 0
BEGIN
SET @MYWEIRDERRORTRAPPER = @ERROR
END
在这种情况下,您基本上吞下了存储过程中的异常。这是一个非常糟糕的做法,IMO,但我过去继承了这样的代码。
答案 2 :(得分:1)
你究竟有多确定this.actOnError_m(ex)永远不会被调用?
另外,你没有正确处理IDisposable,这是我在事情开始变得奇怪时首先要寻找的东西之一。试试这个:
public Task GetNextTask(out int iQueued)
{
iQueued = 0;
Task tskNext;
using (SqlCommand cmdNextTask = new SqlCommand())
{
cmdNextTask.CommandType = System.Data.CommandType.StoredProcedure;
cmdNextTask.CommandText = "pGetNextTask";
using (SqlConnection connection = new System.Data.SqlClient.SqlConnection())
{
cmdNextTask.Connection = connection;
cmdNextTask.Connection.ConnectionString = "my connection string";
cmdNextTask.Connection.Open();
using (DataSet dsNextTask = new DataSet())
{
using (
System.Data.SqlClient.SqlDataAdapter sqlNextTask =
new System.Data.SqlClient.SqlDataAdapter(
cmdNextTask))
{
sqlNextTask.Fill(dsNextTask);
}
tskNext = LoadTask(dsNextTask);
if (dsNextTask.Tables[0].Rows.Count > 0)
{
iQueued = (int) dsNextTask.Tables[0].Rows[0]["Queued"];
}
}
}
}
return tskNext;
}
并不是说我认为这是解决方案,但不用担心这一点。
答案 3 :(得分:0)
我将DataAccessLayer的实例化移出构造函数并移入Main()方法。我从未见过这个问题,所以我认为它解决了这个问题。我认为问题是构造函数发生在主应用程序线程中,因此DataAccessLayer实例位于线程之外。将实例化移动到Main()方法允许线程拥有该对象。