项目中曾经用到开启一个子线程去删除一批数据,结果删除完,子线程关闭后,主线程开始狂报错,到处报“未将对象引用设置到对象的实例”。这个问题很久了也没有仔细去查,最近用户老抱怨,不得不查了。
我的项目将数据库操作对象设为了静态对象,子线程关闭会将改对象状态关闭。所以,在数据库操作类里要区分是在主线程还是在子线程中执行。
private bool IsRuninThread { get { return (System.Threading.Thread.CurrentThread.ApartmentState != System.Threading.ApartmentState.STA); } }
设置这个属性来判断是主线程还是子线程,主线程是STA,子线程是MTA。然后在构造SqlCommand以及执行操作时都要做区分:
/// <summary>
/// 创建数据库指令
/// </summary>
/// <param name="SqlString"></param>
/// <param name="SqlParameters"></param>
/// <returns></returns>
public SqlCommand CreateCommand(string SqlString, SqlParameterList SqlParams)
{
SqlCommand cmd;
if (IsRuninThread) //多线程访问模式
{
SqlConnection con = new SqlConnection(mConnectionString);
cmd = new SqlCommand(SqlString, con);
}
else
{
if (this.sqlTrans == null) cmd = new SqlCommand(SqlString, m_Connection);
else cmd = new SqlCommand(SqlString, m_Connection, this.sqlTrans);
}
this.SetParameters(cmd, SqlParams);
cmd.CommandTimeout = SqlOutTime;
return cmd;
}
public DataSet GetDataSet(IBase Entity, string SqlStr, SqlParameterList SqlParams)
{
if (SqlStr == "") return null;
using (SqlDataAdapter sda = this.CreateDataAdapter(SqlStr, SqlParams))
{
try
{
DataSet ds = new DataSet("QueryDataSet");
sda.Fill(ds);
return ds;
}
catch (Exception ex)
{
if (Entity != null) Entity.ErrorText = ex.Message;
// System.Diagnostics.Debug.Assert(false, "执行SQL出错", SqlStr + "\r\n" + ex.Message);
AppInfo.Debug("执行SQL" + SqlStr + "出错:" + ex.Message);
return null;
}
finally
{
if (IsRuninThread) FreeCommand(sda);
}
}
}
public object ExecuteScalar(IBase Entity, SqlCommand cmd)
{
if (cmd == null) return null;
try
{
if (IsRuninThread)
{
cmd.Connection.Open();
}
else
{
if (cmd.Connection == null) cmd.Connection = this.m_Connection;
this.OpenConnection();
}
return cmd.ExecuteScalar();
}
catch (Exception ex)
{
if (Entity != null) Entity.ErrorText = ex.Message;
// System.Diagnostics.Debug.Assert(false, "执行SQL出错", cmd.CommandText + "\r\n" + ex.Message);
AppInfo.Debug("执行SQL" + cmd.CommandText + "出错:" + ex.Message);
return null;
}
finally
{
if (IsRuninThread) FreeCommand(cmd);
else this.CloseConnection();
}
}
/// <summary>
/// 多线程中清除线程访问的数据实例
/// </summary>
/// <param name="cmd"></param>
private void FreeCommand(SqlCommand cmd)
{
cmd.Connection.Close();
cmd.Connection.Dispose();
cmd.Dispose();
}
/// <summary>
/// 多线程中清除线程访问的数据实例
/// </summary>
/// <param name="apd"></param>
private void FreeCommand(SqlDataAdapter apd)
{
apd.SelectCommand.Connection.Close();
apd.SelectCommand.Connection.Dispose();
apd.Dispose();
}
这样子线程结束就不会影响主线程的正常运行啦!